lib: add Render::{push, write}_borrowed

allow rendering iterators with borrowed events

resolves #24
This commit is contained in:
Noah Hellman 2023-03-17 19:25:24 +01:00
parent e8503e28fd
commit 10788af246

View file

@ -71,6 +71,9 @@ type CowStr<'s> = std::borrow::Cow<'s, str>;
/// ///
/// The output can be written to either a [`std::fmt::Write`] or a [`std::io::Write`] object. /// The output can be written to either a [`std::fmt::Write`] or a [`std::io::Write`] object.
/// ///
/// If ownership of the [`Event`]s cannot be given to the renderer, use [`Render::push_borrowed`]
/// or [`Render::write_borrowed`].
///
/// An implementor needs to at least implement the [`Render::render_event`] function that renders a /// An implementor needs to at least implement the [`Render::render_event`] function that renders a
/// single event to the output. If anything needs to be rendered at the beginning or end of the /// single event to the output. If anything needs to be rendered at the beginning or end of the
/// output, the [`Render::render_prologue`] and [`Render::render_epilogue`] can be implemented as /// output, the [`Render::render_prologue`] and [`Render::render_epilogue`] can be implemented as
@ -136,7 +139,7 @@ pub trait Render {
self.render_epilogue(&mut out) self.render_epilogue(&mut out)
} }
/// Write [`Event`]s to a byte sink, encoded as UTF-8. /// Write owned [`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. /// NOTE: This performs many small writes, so IO writes should be buffered with e.g.
/// [`std::io::BufWriter`]. /// [`std::io::BufWriter`].
@ -145,35 +148,81 @@ pub trait Render {
I: Iterator<Item = Event<'s>>, I: Iterator<Item = Event<'s>>,
W: io::Write, W: io::Write,
{ {
struct Adapter<T: io::Write> { let mut out = WriteAdapter {
inner: T,
error: io::Result<()>,
}
impl<T: io::Write> fmt::Write for Adapter<T> {
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, inner: out,
error: Ok(()), error: Ok(()),
}; };
match self.push(events, &mut out) { self.push(events, &mut out).map_err(|_| match out.error {
Ok(()) => Ok(()), Err(e) => e,
Err(_) => match out.error { _ => io::Error::new(io::ErrorKind::Other, "formatter error"),
Err(_) => out.error, })
_ => Err(io::Error::new(io::ErrorKind::Other, "formatter error")),
},
} }
/// Push borrowed [`Event`]s to a unicode-accepting buffer or stream.
///
/// # Examples
///
/// Render a borrowed slice of [`Event`]s.
/// ```
/// # use jotdown::Render;
/// # let events: &[jotdown::Event] = &[];
/// let mut output = String::new();
/// let mut renderer = jotdown::html::Renderer::default();
/// renderer.push_borrowed(events.iter(), &mut output);
/// ```
fn push_borrowed<'s, E, I, W>(&mut self, mut events: I, mut out: W) -> fmt::Result
where
E: AsRef<Event<'s>>,
I: Iterator<Item = E>,
W: fmt::Write,
{
self.render_prologue(&mut out)?;
events.try_for_each(|e| self.render_event(e.as_ref(), &mut out))?;
self.render_epilogue(&mut out)
}
/// Write borrowed [`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_borrowed<'s, E, I, W>(&mut self, events: I, out: W) -> io::Result<()>
where
E: AsRef<Event<'s>>,
I: Iterator<Item = E>,
W: io::Write,
{
let mut out = WriteAdapter {
inner: out,
error: Ok(()),
};
self.push_borrowed(events, &mut out)
.map_err(|_| match out.error {
Err(e) => e,
_ => io::Error::new(io::ErrorKind::Other, "formatter error"),
})
}
}
struct WriteAdapter<T: io::Write> {
inner: T,
error: io::Result<()>,
}
impl<T: io::Write> fmt::Write for WriteAdapter<T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.inner.write_all(s.as_bytes()).map_err(|e| {
self.error = Err(e);
fmt::Error
})
}
}
// XXX why is this not a blanket implementation?
impl<'s> AsRef<Event<'s>> for &Event<'s> {
fn as_ref(&self) -> &Event<'s> {
self
} }
} }