Skip to content

Commit

Permalink
lib: add Render::{push, write}_ref
Browse files Browse the repository at this point in the history
allow rendering of iterators with borrowed events

resolves #24
  • Loading branch information
hellux committed Mar 19, 2023
1 parent d41d04e commit abeed56
Showing 1 changed file with 74 additions and 17 deletions.
91 changes: 74 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
/// If ownership of the [`Event`]s cannot be given to the renderer, use [`Render::push_ref`] or
/// [`Render::write_ref`].
///
/// 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
/// output, the [`Render::render_prologue`] and [`Render::render_epilogue`] can be implemented as
Expand Down Expand Up @@ -130,7 +133,7 @@ pub trait Render {
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.
/// [`std::io::BufWriter`].
Expand All @@ -139,29 +142,59 @@ pub trait Render {
I: Iterator<Item = Event<'s>>,
W: io::Write,
{
struct Adapter<T: io::Write> {
inner: T,
error: io::Result<()>,
}
let mut out = WriteAdapter {
inner: out,
error: Ok(()),
};

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)
}
}
}
match self.push(events, &mut out) {
Ok(()) => Ok(()),
Err(_) => match out.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_ref(events.iter(), &mut output);
/// ```
fn push_ref<'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)
}

let mut out = Adapter {
/// 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_ref<'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(()),
};

match self.push(events, &mut out) {
match self.push_ref(events, &mut out) {
Ok(()) => Ok(()),
Err(_) => match out.error {
Err(_) => out.error,
Expand All @@ -171,6 +204,30 @@ pub trait Render {
}
}

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 {
match self.inner.write_all(s.as_bytes()) {
Ok(()) => Ok(()),
Err(e) => {
self.error = Err(e);
Err(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
}
}

/// A Djot event.
///
/// A Djot document is represented by a sequence of events. An element may consist of one or
Expand Down

0 comments on commit abeed56

Please sign in to comment.