use core::fmt::Arguments;
use crate::{Error, ErrorKind, Header, HeaderOther, HeaderSpecial, RequestLine, StatusLine};
pub struct FmtWriteAdapter<T> {
inner: T,
err: Written,
}
impl<T> FmtWriteAdapter<T> {
pub fn new(inner: T) -> Self {
Self { inner, err: Ok(()) }
}
}
impl<T> core::fmt::Write for FmtWriteAdapter<T>
where
T: Write + Sized,
{
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.inner.write(s.as_bytes()).map_err(|e| {
self.err = Err(e);
core::fmt::Error
})
}
}
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Written;
fn write_fmt(&mut self, args: Arguments<'_>) -> Written
where
Self: Sized,
{
let mut adp = FmtWriteAdapter::new(self);
let _ = core::fmt::Write::write_fmt(&mut adp, args);
adp.err
}
}
impl<T> Write for &mut T
where
T: Write,
{
fn write(&mut self, buf: &[u8]) -> Written {
(**self).write(buf)
}
}
pub struct WriteCursor<T> {
at: usize,
buf: T,
}
impl<T> WriteCursor<T> {
pub fn new(buf: T) -> Self {
Self { at: 0, buf }
}
pub fn reset(&mut self) {
self.at = 0;
}
pub fn remaining_mut(&mut self) -> &mut [u8]
where
T: AsMut<[u8]>,
{
&mut self.buf.as_mut()[self.at..]
}
pub fn written(&mut self) -> &[u8]
where
T: AsRef<[u8]>,
{
&self.buf.as_ref()[..self.at]
}
}
impl<T> Write for WriteCursor<T>
where
T: AsMut<[u8]>,
{
fn write(&mut self, buf: &[u8]) -> Written {
let remaining = self.remaining_mut();
if buf.len() > remaining.len() {
return Err(Error::from(ErrorKind::BufNotBigEnough));
}
remaining[..buf.len()].copy_from_slice(buf);
self.at += buf.len();
Ok(())
}
}
pub type Written = Result<(), Error>;
pub fn status_line(sl: &StatusLine, mut w: impl Write) -> Written {
write!(
w,
"{} {} {}\r\n",
sl.version, sl.status_code, sl.status_text
)
}
pub fn request_line(rl: &RequestLine, mut w: impl Write) -> Written {
write!(w, "{} {} {}\r\n", rl.method, rl.target, rl.version)
}
pub fn header(h: &Header, mut w: impl Write) -> Written {
match h {
Header::Special(h) => match h {
HeaderSpecial::TransferEncodingChunked => write!(w, "Transfer-Encoding: Chunked"),
HeaderSpecial::ContentLength(cl) => write!(w, "Content-Length: {}", cl),
},
Header::Other(HeaderOther { name, value }) => {
write!(w, "{name}: ")?;
w.write(value)
},
}?;
write!(w, "\r\n")
}