lib: add Render::render_{event, prologue, epilogue}
derive push/write automatically from these
This commit is contained in:
parent
e506fffed8
commit
e8503e28fd
8 changed files with 73 additions and 48 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
27
src/html.rs
27
src/html.rs
|
@ -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,
|
||||||
|
|
68
src/lib.rs
68
src/lib.rs
|
@ -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<()>,
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
Loading…
Reference in a new issue