use core::net::IpAddr;
use super::{Uri, UriInner};
use crate::address::HostRef;
use rama_core::bytes::BytesMut;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WireError {
AsteriskMismatch,
NoScheme,
NoAuthority,
}
impl core::fmt::Display for WireError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::AsteriskMismatch => {
f.write_str("asterisk-form URI cannot be serialised in the requested wire form")
}
Self::NoScheme => f.write_str("requested wire form requires a scheme"),
Self::NoAuthority => f.write_str("requested wire form requires an authority"),
}
}
}
impl core::error::Error for WireError {}
impl Uri {
pub fn write_http_origin_form(&self, buf: &mut BytesMut) -> Result<(), WireError> {
if matches!(self.inner, UriInner::Asterisk) {
return Err(WireError::AsteriskMismatch);
}
write_path_query(self, buf);
Ok(())
}
pub fn write_http_absolute_form(&self, buf: &mut BytesMut) -> Result<(), WireError> {
if matches!(self.inner, UriInner::Asterisk) {
return Err(WireError::AsteriskMismatch);
}
let Some(scheme) = self.scheme() else {
return Err(WireError::NoScheme);
};
buf.extend_from_slice(scheme.as_str().as_bytes());
buf.extend_from_slice(b":");
if self.authority().is_some() {
buf.extend_from_slice(b"//");
write_host_port(self, buf);
}
write_path_query(self, buf);
Ok(())
}
pub fn write_http_authority_form(&self, buf: &mut BytesMut) -> Result<(), WireError> {
if matches!(self.inner, UriInner::Asterisk) {
return Err(WireError::AsteriskMismatch);
}
if self.authority().is_none() {
return Err(WireError::NoAuthority);
}
write_host_port(self, buf);
Ok(())
}
pub fn write_h2_path(&self, buf: &mut BytesMut) {
if matches!(self.inner, UriInner::Asterisk) {
buf.extend_from_slice(b"*");
return;
}
write_path_query(self, buf);
}
pub fn write_h2_authority(&self, buf: &mut BytesMut) -> Result<(), WireError> {
if matches!(self.inner, UriInner::Asterisk) {
return Err(WireError::AsteriskMismatch);
}
if self.authority().is_none() {
return Err(WireError::NoAuthority);
}
write_host_port(self, buf);
Ok(())
}
pub fn write_h2_scheme(&self, buf: &mut BytesMut) -> Result<(), WireError> {
if matches!(self.inner, UriInner::Asterisk) {
return Err(WireError::AsteriskMismatch);
}
let Some(scheme) = self.scheme() else {
return Err(WireError::NoScheme);
};
buf.extend_from_slice(scheme.as_str().as_bytes());
Ok(())
}
}
fn write_host_port(uri: &Uri, buf: &mut BytesMut) {
use core::fmt::Write as _;
if let Some(host) = uri.host() {
match host {
HostRef::Name(d) => buf.extend_from_slice(d.as_bytes()),
HostRef::Address(IpAddr::V4(v4)) => {
#[expect(
clippy::let_underscore_must_use,
reason = "BytesMutWriter::write_str is infallible by construction"
)]
let _ = write!(BytesMutWriter(buf), "{v4}");
}
HostRef::Address(IpAddr::V6(v6)) => {
buf.extend_from_slice(b"[");
#[expect(
clippy::let_underscore_must_use,
reason = "BytesMutWriter::write_str is infallible by construction"
)]
let _ = write!(BytesMutWriter(buf), "{v6}");
buf.extend_from_slice(b"]");
}
HostRef::Uninterpreted(host) => {
if host.is_bracketed() {
buf.extend_from_slice(b"[");
buf.extend_from_slice(host.as_bytes());
buf.extend_from_slice(b"]");
} else {
buf.extend_from_slice(host.as_bytes());
}
}
}
}
match uri.port() {
crate::address::OptPort::Unset => {}
crate::address::OptPort::Empty => {
buf.extend_from_slice(b":");
}
crate::address::OptPort::Set(port) => {
buf.extend_from_slice(b":");
let mut itoa = itoa::Buffer::new();
buf.extend_from_slice(itoa.format(port).as_bytes());
}
}
}
struct BytesMutWriter<'a>(&'a mut BytesMut);
impl core::fmt::Write for BytesMutWriter<'_> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.0.extend_from_slice(s.as_bytes());
Ok(())
}
}
fn write_path_query(uri: &Uri, buf: &mut BytesMut) {
if let Some(path) = uri.path().filter(|p| !p.is_empty()) {
path.write_encoded_to(buf);
} else {
buf.extend_from_slice(b"/");
}
if let Some(q) = uri.query() {
buf.extend_from_slice(b"?");
q.write_encoded_to(buf);
}
}