diff --git a/src/lib.rs b/src/lib.rs index ce410fe..6e61c98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,9 @@ //! # } //! ``` -use std::fmt::Write; +use std::fmt; +use std::fmt::Write as FmtWrite; +use std::io; #[cfg(feature = "html")] pub mod html; @@ -63,6 +65,55 @@ pub use attr::Attributes; type CowStr<'s> = std::borrow::Cow<'s, str>; +pub trait Render { + /// Push [`Event`]s to a unicode-accepting buffer or stream. + fn push<'s, I: Iterator>, W: fmt::Write>( + &self, + events: I, + out: W, + ) -> fmt::Result; + + /// Write [`Event`]s to a byte sink, encoded as UTF-8. + /// + /// NOTE: This performs many small writes, so IO writes should be buffered with e.g. + /// [`std::io::BufWriter`]. + fn write<'s, I: Iterator>, W: io::Write>( + &self, + events: I, + out: W, + ) -> io::Result<()> { + struct Adapter { + inner: T, + error: io::Result<()>, + } + + impl fmt::Write for Adapter { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut out = Adapter { + inner: out, + error: Ok(()), + }; + + match self.push(events, &mut out) { + Ok(()) => Ok(()), + Err(_) => match out.error { + Err(_) => out.error, + _ => Err(io::Error::new(io::ErrorKind::Other, "formatter error")), + }, + } + } +} + /// A Djot event. /// /// A Djot document is represented by a sequence of events. An element may consist of one or