From e8503e28fd775e47543d621ed7eaddcff1c8f056 Mon Sep 17 00:00:00 2001 From: Noah Hellman Date: Sun, 19 Mar 2023 18:44:58 +0100 Subject: [PATCH] lib: add Render::render_{event, prologue, epilogue} derive push/write automatically from these --- bench/criterion/main.rs | 6 ++- bench/iai/main.rs | 2 +- examples/jotdown_wasm/src/lib.rs | 4 +- src/html.rs | 27 ++----------- src/lib.rs | 68 +++++++++++++++++++++++++------- src/main.rs | 6 +-- tests/afl/src/lib.rs | 4 +- tests/lib.rs | 4 +- 8 files changed, 73 insertions(+), 48 deletions(-) diff --git a/bench/criterion/main.rs b/bench/criterion/main.rs index 5ff477f..28c2a4f 100644 --- a/bench/criterion/main.rs +++ b/bench/criterion/main.rs @@ -51,7 +51,9 @@ fn gen_html(c: &mut criterion::Criterion) { || jotdown::Parser::new(input).collect::>(), |p| { let mut s = String::new(); - jotdown::html::Renderer.push(p.into_iter(), &mut s).unwrap(); + jotdown::html::Renderer::default() + .push(p.into_iter(), &mut s) + .unwrap(); s }, criterion::BatchSize::SmallInput, @@ -72,7 +74,7 @@ fn gen_full(c: &mut criterion::Criterion) { |b, &input| { b.iter_with_large_drop(|| { let mut s = String::new(); - jotdown::html::Renderer + jotdown::html::Renderer::default() .push(jotdown::Parser::new(input), &mut s) .unwrap(); s diff --git a/bench/iai/main.rs b/bench/iai/main.rs index e606d5f..d948bb6 100644 --- a/bench/iai/main.rs +++ b/bench/iai/main.rs @@ -12,7 +12,7 @@ fn block_inline() -> Option> { fn full() -> String { let mut s = String::new(); - jotdown::html::Renderer + jotdown::html::Renderer::default() .push(jotdown::Parser::new(bench_input::ALL), &mut s) .unwrap(); s diff --git a/examples/jotdown_wasm/src/lib.rs b/examples/jotdown_wasm/src/lib.rs index 3ab7fb0..5250cf1 100644 --- a/examples/jotdown_wasm/src/lib.rs +++ b/examples/jotdown_wasm/src/lib.rs @@ -7,6 +7,8 @@ use jotdown::Render; pub fn jotdown_render(djot: &str) -> String { let events = jotdown::Parser::new(djot); let mut html = String::new(); - jotdown::html::Renderer.push(events, &mut html).unwrap(); + jotdown::html::Renderer::default() + .push(events, &mut html) + .unwrap(); html } diff --git a/src/html.rs b/src/html.rs index 0014ef3..bd105c8 100644 --- a/src/html.rs +++ b/src/html.rs @@ -9,25 +9,13 @@ use crate::OrderedListNumbering::*; use crate::Render; use crate::SpanLinkType; -pub struct Renderer; - -impl Render for Renderer { - fn push<'s, I: Iterator>, W: std::fmt::Write>( - &self, - events: I, - out: W, - ) -> std::fmt::Result { - Writer::default().write(events, out) - } -} - enum Raw { None, Html, Other, } -struct Writer { +pub struct Renderer { raw: Raw, img_alt_text: usize, list_tightness: Vec, @@ -37,7 +25,7 @@ struct Writer { close_para: bool, } -impl Default for Writer { +impl Default for Renderer { fn default() -> Self { Self { raw: Raw::None, @@ -51,16 +39,7 @@ impl Default for Writer { } } -impl Writer { - fn write<'s, I, W>(&mut self, mut events: I, mut out: W) -> std::fmt::Result - where - I: Iterator>, - W: std::fmt::Write, - { - events.try_for_each(|e| self.render_event(&e, &mut out))?; - self.render_epilogue(&mut out) - } - +impl Render for Renderer { fn render_event<'s, W>(&mut self, e: &Event<'s>, mut out: W) -> std::fmt::Result where W: std::fmt::Write, diff --git a/src/lib.rs b/src/lib.rs index eb41f12..d8aa6f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ //! let djot_input = "hello *world*!"; //! let events = jotdown::Parser::new(djot_input); //! let mut html = String::new(); -//! jotdown::html::Renderer.push(events, &mut html); +//! jotdown::html::Renderer::default().push(events, &mut html); //! assert_eq!(html, "

hello world!

\n"); //! # } //! ``` @@ -41,7 +41,7 @@ //! e => e, //! }); //! let mut html = String::new(); -//! jotdown::html::Renderer.push(events, &mut html); +//! jotdown::html::Renderer::default().push(events, &mut html); //! assert_eq!(html, "

a link

\n"); //! # } //! ``` @@ -71,6 +71,11 @@ 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. /// +/// 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 +/// well. +/// /// # Examples /// /// Push to a [`String`] (implements [`std::fmt::Write`]): @@ -79,7 +84,8 @@ type CowStr<'s> = std::borrow::Cow<'s, str>; /// # use jotdown::Render; /// # let events = std::iter::empty(); /// let mut output = String::new(); -/// jotdown::html::Renderer.push(events, &mut output); +/// let mut renderer = jotdown::html::Renderer::default(); +/// renderer.push(events, &mut output); /// ``` /// /// Write to standard output with buffering ([`std::io::Stdout`] implements [`std::io::Write`]): @@ -88,25 +94,57 @@ type CowStr<'s> = std::borrow::Cow<'s, str>; /// # use jotdown::Render; /// # let events = std::iter::empty(); /// let mut out = std::io::BufWriter::new(std::io::stdout()); -/// jotdown::html::Renderer.write(events, &mut out).unwrap(); +/// let mut renderer = jotdown::html::Renderer::default(); +/// renderer.write(events, &mut out).unwrap(); /// ``` 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; + /// Render a single event. + fn render_event<'s, W>(&mut self, e: &Event<'s>, out: W) -> std::fmt::Result + where + W: std::fmt::Write; + + /// Render something before any events have been provided. + /// + /// This does nothing by default, but an implementation may choose to prepend data at the + /// beginning of the output if needed. + fn render_prologue(&mut self, _out: W) -> std::fmt::Result + where + W: std::fmt::Write, + { + Ok(()) + } + + /// Render something after all events have been provided. + /// + /// This does nothing by default, but an implementation may choose to append extra data at the + /// end of the output if needed. + fn render_epilogue(&mut self, _out: W) -> std::fmt::Result + where + W: std::fmt::Write, + { + Ok(()) + } + + /// Push owned [`Event`]s to a unicode-accepting buffer or stream. + fn push<'s, I, W>(&mut self, mut events: I, mut out: W) -> fmt::Result + where + I: Iterator>, + W: fmt::Write, + { + self.render_prologue(&mut out)?; + events.try_for_each(|e| self.render_event(&e, &mut out))?; + self.render_epilogue(&mut out) + } /// 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<()> { + fn write<'s, I, W>(&mut self, events: I, out: W) -> io::Result<()> + where + I: Iterator>, + W: io::Write, + { struct Adapter { inner: T, error: io::Result<()>, diff --git a/src/main.rs b/src/main.rs index b9ea08c..e73c081 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,11 +68,11 @@ fn run() -> Result<(), std::io::Error> { }; let parser = jotdown::Parser::new(&content); - let html = jotdown::html::Renderer; + let mut renderer = jotdown::html::Renderer::default(); match app.output { - Some(path) => html.write(parser, File::create(path)?)?, - None => html.write(parser, BufWriter::new(std::io::stdout()))?, + Some(path) => renderer.write(parser, File::create(path)?)?, + None => renderer.write(parser, BufWriter::new(std::io::stdout()))?, } Ok(()) diff --git a/tests/afl/src/lib.rs b/tests/afl/src/lib.rs index 530a6ae..adbca14 100644 --- a/tests/afl/src/lib.rs +++ b/tests/afl/src/lib.rs @@ -19,7 +19,9 @@ pub fn html(data: &[u8]) { if !s.contains("=html") { let p = jotdown::Parser::new(s); let mut html = "\n".to_string(); - jotdown::html::Renderer.push(p, &mut html).unwrap(); + jotdown::html::Renderer::default() + .push(p, &mut html) + .unwrap(); validate_html(&html); } } diff --git a/tests/lib.rs b/tests/lib.rs index 984b610..4fd36af 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -14,7 +14,9 @@ macro_rules! suite_test { let expected = $expected; let p = jotdown::Parser::new(src); let mut actual = String::new(); - jotdown::html::Renderer.push(p, &mut actual).unwrap(); + jotdown::html::Renderer::default() + .push(p, &mut actual) + .unwrap(); assert_eq!( actual.trim(), expected.trim(),