lib: add Render::render_{event, prologue, epilogue}

derive push/write automatically from these
This commit is contained in:
Noah Hellman 2023-03-19 18:44:58 +01:00
parent e506fffed8
commit e8503e28fd
8 changed files with 73 additions and 48 deletions

View file

@ -51,7 +51,9 @@ fn gen_html(c: &mut criterion::Criterion) {
|| jotdown::Parser::new(input).collect::<Vec<_>>(), || jotdown::Parser::new(input).collect::<Vec<_>>(),
|p| { |p| {
let mut s = String::new(); 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 s
}, },
criterion::BatchSize::SmallInput, criterion::BatchSize::SmallInput,
@ -72,7 +74,7 @@ fn gen_full(c: &mut criterion::Criterion) {
|b, &input| { |b, &input| {
b.iter_with_large_drop(|| { b.iter_with_large_drop(|| {
let mut s = String::new(); let mut s = String::new();
jotdown::html::Renderer jotdown::html::Renderer::default()
.push(jotdown::Parser::new(input), &mut s) .push(jotdown::Parser::new(input), &mut s)
.unwrap(); .unwrap();
s s

View file

@ -12,7 +12,7 @@ fn block_inline() -> Option<jotdown::Event<'static>> {
fn full() -> String { fn full() -> String {
let mut s = String::new(); let mut s = String::new();
jotdown::html::Renderer jotdown::html::Renderer::default()
.push(jotdown::Parser::new(bench_input::ALL), &mut s) .push(jotdown::Parser::new(bench_input::ALL), &mut s)
.unwrap(); .unwrap();
s s

View file

@ -7,6 +7,8 @@ use jotdown::Render;
pub fn jotdown_render(djot: &str) -> String { pub fn jotdown_render(djot: &str) -> String {
let events = jotdown::Parser::new(djot); let events = jotdown::Parser::new(djot);
let mut html = String::new(); let mut html = String::new();
jotdown::html::Renderer.push(events, &mut html).unwrap(); jotdown::html::Renderer::default()
.push(events, &mut html)
.unwrap();
html html
} }

View file

@ -9,25 +9,13 @@ use crate::OrderedListNumbering::*;
use crate::Render; use crate::Render;
use crate::SpanLinkType; use crate::SpanLinkType;
pub struct Renderer;
impl Render for Renderer {
fn push<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write>(
&self,
events: I,
out: W,
) -> std::fmt::Result {
Writer::default().write(events, out)
}
}
enum Raw { enum Raw {
None, None,
Html, Html,
Other, Other,
} }
struct Writer { pub struct Renderer {
raw: Raw, raw: Raw,
img_alt_text: usize, img_alt_text: usize,
list_tightness: Vec<bool>, list_tightness: Vec<bool>,
@ -37,7 +25,7 @@ struct Writer {
close_para: bool, close_para: bool,
} }
impl Default for Writer { impl Default for Renderer {
fn default() -> Self { fn default() -> Self {
Self { Self {
raw: Raw::None, raw: Raw::None,
@ -51,16 +39,7 @@ impl Default for Writer {
} }
} }
impl Writer { impl Render for Renderer {
fn write<'s, I, W>(&mut self, mut events: I, mut out: W) -> std::fmt::Result
where
I: Iterator<Item = Event<'s>>,
W: std::fmt::Write,
{
events.try_for_each(|e| self.render_event(&e, &mut out))?;
self.render_epilogue(&mut out)
}
fn render_event<'s, W>(&mut self, e: &Event<'s>, mut out: W) -> std::fmt::Result fn render_event<'s, W>(&mut self, e: &Event<'s>, mut out: W) -> std::fmt::Result
where where
W: std::fmt::Write, W: std::fmt::Write,

View file

@ -20,7 +20,7 @@
//! let djot_input = "hello *world*!"; //! let djot_input = "hello *world*!";
//! let events = jotdown::Parser::new(djot_input); //! let events = jotdown::Parser::new(djot_input);
//! let mut html = String::new(); //! let mut html = String::new();
//! jotdown::html::Renderer.push(events, &mut html); //! jotdown::html::Renderer::default().push(events, &mut html);
//! assert_eq!(html, "<p>hello <strong>world</strong>!</p>\n"); //! assert_eq!(html, "<p>hello <strong>world</strong>!</p>\n");
//! # } //! # }
//! ``` //! ```
@ -41,7 +41,7 @@
//! e => e, //! e => e,
//! }); //! });
//! let mut html = String::new(); //! let mut html = String::new();
//! jotdown::html::Renderer.push(events, &mut html); //! jotdown::html::Renderer::default().push(events, &mut html);
//! assert_eq!(html, "<p>a <a href=\"https://example.net\">link</a></p>\n"); //! assert_eq!(html, "<p>a <a href=\"https://example.net\">link</a></p>\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. /// 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 /// # Examples
/// ///
/// Push to a [`String`] (implements [`std::fmt::Write`]): /// Push to a [`String`] (implements [`std::fmt::Write`]):
@ -79,7 +84,8 @@ type CowStr<'s> = std::borrow::Cow<'s, str>;
/// # use jotdown::Render; /// # use jotdown::Render;
/// # let events = std::iter::empty(); /// # let events = std::iter::empty();
/// let mut output = String::new(); /// 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`]): /// 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; /// # use jotdown::Render;
/// # let events = std::iter::empty(); /// # let events = std::iter::empty();
/// let mut out = std::io::BufWriter::new(std::io::stdout()); /// 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 { pub trait Render {
/// Push [`Event`]s to a unicode-accepting buffer or stream. /// Render a single event.
fn push<'s, I: Iterator<Item = Event<'s>>, W: fmt::Write>( fn render_event<'s, W>(&mut self, e: &Event<'s>, out: W) -> std::fmt::Result
&self, where
events: I, W: std::fmt::Write;
out: W,
) -> fmt::Result; /// 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<W>(&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<W>(&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<Item = Event<'s>>,
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. /// 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. /// NOTE: This performs many small writes, so IO writes should be buffered with e.g.
/// [`std::io::BufWriter`]. /// [`std::io::BufWriter`].
fn write<'s, I: Iterator<Item = Event<'s>>, W: io::Write>( fn write<'s, I, W>(&mut self, events: I, out: W) -> io::Result<()>
&self, where
events: I, I: Iterator<Item = Event<'s>>,
out: W, W: io::Write,
) -> io::Result<()> { {
struct Adapter<T: io::Write> { struct Adapter<T: io::Write> {
inner: T, inner: T,
error: io::Result<()>, error: io::Result<()>,

View file

@ -68,11 +68,11 @@ fn run() -> Result<(), std::io::Error> {
}; };
let parser = jotdown::Parser::new(&content); let parser = jotdown::Parser::new(&content);
let html = jotdown::html::Renderer; let mut renderer = jotdown::html::Renderer::default();
match app.output { match app.output {
Some(path) => html.write(parser, File::create(path)?)?, Some(path) => renderer.write(parser, File::create(path)?)?,
None => html.write(parser, BufWriter::new(std::io::stdout()))?, None => renderer.write(parser, BufWriter::new(std::io::stdout()))?,
} }
Ok(()) Ok(())

View file

@ -19,7 +19,9 @@ pub fn html(data: &[u8]) {
if !s.contains("=html") { if !s.contains("=html") {
let p = jotdown::Parser::new(s); let p = jotdown::Parser::new(s);
let mut html = "<!DOCTYPE html>\n".to_string(); let mut html = "<!DOCTYPE html>\n".to_string();
jotdown::html::Renderer.push(p, &mut html).unwrap(); jotdown::html::Renderer::default()
.push(p, &mut html)
.unwrap();
validate_html(&html); validate_html(&html);
} }
} }

View file

@ -14,7 +14,9 @@ macro_rules! suite_test {
let expected = $expected; let expected = $expected;
let p = jotdown::Parser::new(src); let p = jotdown::Parser::new(src);
let mut actual = String::new(); let mut actual = String::new();
jotdown::html::Renderer.push(p, &mut actual).unwrap(); jotdown::html::Renderer::default()
.push(p, &mut actual)
.unwrap();
assert_eq!( assert_eq!(
actual.trim(), actual.trim(),
expected.trim(), expected.trim(),