From 69de17ad9bbc312c1a79da293cae0757c4ffa8c5 Mon Sep 17 00:00:00 2001 From: PoiScript Date: Thu, 14 Feb 2019 13:38:48 +0800 Subject: [PATCH] refactor(export): HtmlRender & HtmlHandler --- Cargo.toml | 3 + README.md | 39 +++--- examples/convert.rs | 33 ----- examples/custom_handler.rs | 49 +++++++ src/export/html.rs | 5 +- src/export/mod.rs | 275 ++++++++++++++++--------------------- src/lib.rs | 79 ++++++++--- 7 files changed, 246 insertions(+), 237 deletions(-) delete mode 100644 examples/convert.rs create mode 100644 examples/custom_handler.rs diff --git a/Cargo.toml b/Cargo.toml index 9e28699..1d41910 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,6 @@ travis-ci = { repository = "PoiScript/orgize" } bytecount = "0.5.1" jetscii = "0.4.3" memchr = "2" + +[dev-dependencies] +slugify = "0.1.0" diff --git a/README.md b/README.md index 4f2d799..a1baef2 100644 --- a/README.md +++ b/README.md @@ -23,52 +23,45 @@ extern crate orgize; ```rust use orgize::Parser; -fn main() { - let parser = Parser::new( - r#"* Title 1 +let parser = Parser::new( + r"* Title 1 *Section 1* ** Title 2 _Section 2_ * Title 3 /Section 3/ * Title 4 -=Section 4="#, - ); +=Section 4=", +); - for event in parser { - // handling the event - } +for event in parser { + // handling the event } ``` Alternatively, you can use the built-in render. ```rust -use orgize::export::{HtmlHandler, Render}; +use orgize::export::DefaultHtmlRender; use std::io::Cursor; -fn main() { - let contents = r#"* Title 1 +let contents = r"* Title 1 *Section 1* ** Title 2 _Section 2_ * Title 3 /Section 3/ * Title 4 -=Section 4="#; +=Section 4="; - let cursor = Cursor::new(Vec::new()); - let mut render = Render::new(HtmlHandler, cursor, &contents); +let cursor = Cursor::new(Vec::new()); +let mut render = DefaultHtmlRender::new(cursor, &contents); - render - .render() - .expect("something went wrong rendering the file"); - - println!( - "{}", - String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8") - ); -} +render + .render() + .expect("something went wrong rendering the file"); + +let result = String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8"); ``` ## License diff --git a/examples/convert.rs b/examples/convert.rs deleted file mode 100644 index 5e10cac..0000000 --- a/examples/convert.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::Cursor; -use std::io::Read; - -use orgize::export::{HtmlHandler, Render}; - -fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - println!("Usage: {} ", args[0]); - return; - } - - let mut file = File::open(&args[1]).expect(&format!("file {} not found", &args[1])); - - let mut contents = String::new(); - file.read_to_string(&mut contents) - .expect("something went wrong reading the file"); - - let cursor = Cursor::new(Vec::new()); - let handler = HtmlHandler; - let mut render = Render::new(handler, cursor, &contents); - - render - .render() - .expect("something went wrong rendering the file"); - println!( - "{}", - String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8") - ); -} diff --git a/examples/custom_handler.rs b/examples/custom_handler.rs new file mode 100644 index 0000000..edeaa23 --- /dev/null +++ b/examples/custom_handler.rs @@ -0,0 +1,49 @@ +use std::env::args; +use std::fs::File; +use std::io::{Cursor, Read, Result, Write}; + +use orgize::export::*; +use orgize::headline::Headline; +use slugify::slugify; + +struct CustomHtmlHandler; + +impl HtmlHandler for CustomHtmlHandler { + fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()> { + write!( + w, + r##"{2}"##, + if hdl.level <= 6 { hdl.level } else { 6 }, + slugify!(hdl.title), + hdl.title, + ) + } +} + +fn main() -> Result<()> { + let args: Vec<_> = args().collect(); + + if args.len() < 2 { + println!("Usage: {} ", args[0]); + } else { + let mut file = File::open(&args[1])?; + + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + + let cursor = Cursor::new(Vec::new()); + + //let mut render = DefaultHtmlRender::new(cursor, &contents); + // comment the following line and uncomment the line above to use the default handler + let mut render = HtmlRender::new(CustomHtmlHandler, cursor, &contents); + + render.render()?; + + println!( + "{}", + String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8") + ); + } + + Ok(()) +} diff --git a/src/export/html.rs b/src/export/html.rs index 7c4c301..961f96c 100644 --- a/src/export/html.rs +++ b/src/export/html.rs @@ -1,14 +1,11 @@ #![allow(unused_variables)] use crate::elements::Key; -use crate::export::Handler; use crate::headline::Headline; use crate::objects::Cookie; use std::io::{Result, Write}; -pub struct HtmlHandler; - -impl Handler for HtmlHandler { +pub trait HtmlHandler { fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()> { let level = if hdl.level <= 6 { hdl.level } else { 6 }; write!(w, "{1}", level, hdl.title) diff --git a/src/export/mod.rs b/src/export/mod.rs index fbb53a8..05f5da2 100644 --- a/src/export/mod.rs +++ b/src/export/mod.rs @@ -2,171 +2,134 @@ mod html; pub use self::html::HtmlHandler; -use crate::elements::Key; -use crate::headline::Headline; -use crate::objects::Cookie; use crate::parser::Parser; use std::io::{Result, Write}; -pub trait Handler { - fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()>; - fn handle_headline_end(&mut self, w: &mut W) -> Result<()>; - fn handle_section_beg(&mut self, w: &mut W) -> Result<()>; - fn handle_section_end(&mut self, w: &mut W) -> Result<()>; - fn handle_paragraph_beg(&mut self, w: &mut W) -> Result<()>; - fn handle_paragraph_end(&mut self, w: &mut W) -> Result<()>; - fn handle_ctr_block_beg(&mut self, w: &mut W) -> Result<()>; - fn handle_ctr_block_end(&mut self, w: &mut W) -> Result<()>; - fn handle_qte_block_beg(&mut self, w: &mut W) -> Result<()>; - fn handle_qte_block_end(&mut self, w: &mut W) -> Result<()>; - fn handle_spl_block_beg(&mut self, w: &mut W, name: &str, args: Option<&str>) -> Result<()>; - fn handle_spl_block_end(&mut self, w: &mut W) -> Result<()>; - fn handle_comment_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<()>; - fn handle_example_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<()>; - fn handle_export_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<()>; - fn handle_src_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<()>; - fn handle_verse_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<()>; - fn handle_dyn_block_beg(&mut self, w: &mut W, name: &str, args: Option<&str>) -> Result<()>; - fn handle_dyn_block_end(&mut self, w: &mut W) -> Result<()>; - fn handle_list_beg(&mut self, w: &mut W, ordered: bool) -> Result<()>; - fn handle_list_end(&mut self, w: &mut W, ordered: bool) -> Result<()>; - fn handle_list_beg_item(&mut self, w: &mut W, bullet: &str) -> Result<()>; - fn handle_list_end_item(&mut self, w: &mut W) -> Result<()>; - fn handle_call(&mut self, w: &mut W, value: &str) -> Result<()>; - fn handle_clock(&mut self, w: &mut W) -> Result<()>; - fn handle_comment(&mut self, w: &mut W, cont: &str) -> Result<()>; - fn handle_fixed_width(&mut self, w: &mut W, cont: &str) -> Result<()>; - fn handle_table_start(&mut self, w: &mut W) -> Result<()>; - fn handle_table_end(&mut self, w: &mut W) -> Result<()>; - fn handle_table_cell(&mut self, w: &mut W) -> Result<()>; - fn handle_latex_env(&mut self, w: &mut W) -> Result<()>; - fn handle_fn_def(&mut self, w: &mut W, label: &str, cont: &str) -> Result<()>; - fn handle_keyword(&mut self, w: &mut W, key: Key<'_>, value: &str) -> Result<()>; - fn handle_rule(&mut self, w: &mut W) -> Result<()>; - fn handle_cookie(&mut self, w: &mut W, cookie: Cookie) -> Result<()>; - fn handle_fn_ref(&mut self, w: &mut W, label: Option<&str>, def: Option<&str>) -> Result<()>; - fn handle_inline_call( - &mut self, - w: &mut W, - name: &str, - args: &str, - inside_header: Option<&str>, - end_header: Option<&str>, - ) -> Result<()>; - fn handle_inline_src( - &mut self, - w: &mut W, - lang: &str, - option: Option<&str>, - body: &str, - ) -> Result<()>; - fn handle_link(&mut self, w: &mut W, path: &str, desc: Option<&str>) -> Result<()>; - fn handle_macros(&mut self, w: &mut W, name: &str, args: Option<&str>) -> Result<()>; - fn handle_radio_target(&mut self, w: &mut W, target: &str) -> Result<()>; - fn handle_snippet(&mut self, w: &mut W, name: &str, value: &str) -> Result<()>; - fn handle_target(&mut self, w: &mut W, target: &str) -> Result<()>; - fn handle_bold_beg(&mut self, w: &mut W) -> Result<()>; - fn handle_bold_end(&mut self, w: &mut W) -> Result<()>; - fn handle_italic_beg(&mut self, w: &mut W) -> Result<()>; - fn handle_italic_end(&mut self, w: &mut W) -> Result<()>; - fn handle_strike_beg(&mut self, w: &mut W) -> Result<()>; - fn handle_strike_end(&mut self, w: &mut W) -> Result<()>; - fn handle_underline_beg(&mut self, w: &mut W) -> Result<()>; - fn handle_underline_end(&mut self, w: &mut W) -> Result<()>; - fn handle_verbatim(&mut self, w: &mut W, cont: &str) -> Result<()>; - fn handle_code(&mut self, w: &mut W, cont: &str) -> Result<()>; - fn handle_text(&mut self, w: &mut W, cont: &str) -> Result<()>; -} +macro_rules! create_render { + ($handler:ident, $default_handler:ident, $render:ident, $default_render:ident) => { + struct $default_handler; -pub struct Render<'a, W: Write, H: Handler> { - pub parser: Parser<'a>, - pub handler: H, - writer: W, -} + impl $handler for $default_handler {} -impl<'a, W: Write, H: Handler> Render<'a, W, H> { - pub fn new(handler: H, writer: W, text: &'a str) -> Render<'a, W, H> { - Render { - parser: Parser::new(text), - handler, - writer, - } - } + pub struct $default_render<'a, W: Write>($render<'a, W, $default_handler>); - pub fn into_wirter(self) -> W { - self.writer - } + impl<'a, W: Write> $default_render<'a, W> { + #[inline] + pub fn new(writer: W, text: &'a str) -> Self { + $default_render($render::new($default_handler, writer, text)) + } - pub fn render(&mut self) -> Result<()> { - use crate::parser::Event::*; + #[inline] + pub fn into_wirter(self) -> W { + self.0.writer + } - let w = &mut self.writer; - let h = &mut self.handler; - - for event in &mut self.parser { - match event { - HeadlineBeg(hdl) => h.handle_headline_beg(w, hdl)?, - HeadlineEnd => h.handle_headline_end(w)?, - SectionBeg => h.handle_section_beg(w)?, - SectionEnd => h.handle_section_end(w)?, - ParagraphBeg => h.handle_paragraph_beg(w)?, - ParagraphEnd => h.handle_paragraph_end(w)?, - CtrBlockBeg => h.handle_ctr_block_beg(w)?, - CtrBlockEnd => h.handle_ctr_block_end(w)?, - QteBlockBeg => h.handle_qte_block_beg(w)?, - QteBlockEnd => h.handle_qte_block_end(w)?, - SplBlockBeg { name, args } => h.handle_spl_block_beg(w, name, args)?, - SplBlockEnd => h.handle_spl_block_end(w)?, - CommentBlock { cont, args } => h.handle_comment_block(w, cont, args)?, - ExampleBlock { cont, args } => h.handle_example_block(w, cont, args)?, - ExportBlock { cont, args } => h.handle_export_block(w, cont, args)?, - SrcBlock { cont, args } => h.handle_src_block(w, cont, args)?, - VerseBlock { cont, args } => h.handle_verse_block(w, cont, args)?, - DynBlockBeg { name, args } => h.handle_dyn_block_beg(w, name, args)?, - DynBlockEnd => h.handle_dyn_block_end(w)?, - ListBeg { ordered } => h.handle_list_beg(w, ordered)?, - ListEnd { ordered } => h.handle_list_end(w, ordered)?, - ListItemBeg { bullet } => h.handle_list_beg_item(w, bullet)?, - ListItemEnd => h.handle_list_end_item(w)?, - Call { value } => h.handle_call(w, value)?, - Clock => h.handle_clock(w)?, - Comment(c) => h.handle_comment(w, c)?, - FixedWidth(f) => h.handle_fixed_width(w, f)?, - TableStart => h.handle_table_start(w)?, - TableEnd => h.handle_table_end(w)?, - TableCell => h.handle_table_cell(w)?, - LatexEnv => h.handle_latex_env(w)?, - FnDef { label, cont } => h.handle_fn_def(w, label, cont)?, - Keyword { key, value } => h.handle_keyword(w, key, value)?, - Rule => h.handle_rule(w)?, - Cookie(cookie) => h.handle_cookie(w, cookie)?, - FnRef { label, def } => h.handle_fn_ref(w, label, def)?, - InlineSrc { lang, option, body } => h.handle_inline_src(w, lang, option, body)?, - InlineCall { - name, - args, - inside_header, - end_header, - } => h.handle_inline_call(w, name, args, inside_header, end_header)?, - Link { path, desc } => h.handle_link(w, path, desc)?, - Macros { name, args } => h.handle_macros(w, name, args)?, - RadioTarget { target } => h.handle_radio_target(w, target)?, - Snippet { name, value } => h.handle_snippet(w, name, value)?, - Target { target } => h.handle_target(w, target)?, - BoldBeg => h.handle_bold_beg(w)?, - BoldEnd => h.handle_bold_end(w)?, - ItalicBeg => h.handle_italic_beg(w)?, - ItalicEnd => h.handle_italic_end(w)?, - StrikeBeg => h.handle_strike_beg(w)?, - StrikeEnd => h.handle_strike_end(w)?, - UnderlineBeg => h.handle_underline_beg(w)?, - UnderlineEnd => h.handle_underline_end(w)?, - Verbatim(cont) => h.handle_verbatim(w, cont)?, - Code(cont) => h.handle_code(w, cont)?, - Text(cont) => h.handle_text(w, cont)?, + #[inline] + pub fn render(&mut self) -> Result<()> { + self.0.render() } } - Ok(()) - } + pub struct $render<'a, W: Write, H: $handler> { + pub parser: Parser<'a>, + handler: H, + writer: W, + } + + impl<'a, W: Write, H: $handler> $render<'a, W, H> { + pub fn new(handler: H, writer: W, text: &'a str) -> Self { + $render { + parser: Parser::new(text), + handler, + writer, + } + } + + pub fn into_wirter(self) -> W { + self.writer + } + + pub fn render(&mut self) -> Result<()> { + use crate::parser::Event::*; + + let w = &mut self.writer; + let h = &mut self.handler; + + for event in &mut self.parser { + match event { + HeadlineBeg(hdl) => h.handle_headline_beg(w, hdl)?, + HeadlineEnd => h.handle_headline_end(w)?, + SectionBeg => h.handle_section_beg(w)?, + SectionEnd => h.handle_section_end(w)?, + ParagraphBeg => h.handle_paragraph_beg(w)?, + ParagraphEnd => h.handle_paragraph_end(w)?, + CtrBlockBeg => h.handle_ctr_block_beg(w)?, + CtrBlockEnd => h.handle_ctr_block_end(w)?, + QteBlockBeg => h.handle_qte_block_beg(w)?, + QteBlockEnd => h.handle_qte_block_end(w)?, + SplBlockBeg { name, args } => h.handle_spl_block_beg(w, name, args)?, + SplBlockEnd => h.handle_spl_block_end(w)?, + CommentBlock { cont, args } => h.handle_comment_block(w, cont, args)?, + ExampleBlock { cont, args } => h.handle_example_block(w, cont, args)?, + ExportBlock { cont, args } => h.handle_export_block(w, cont, args)?, + SrcBlock { cont, args } => h.handle_src_block(w, cont, args)?, + VerseBlock { cont, args } => h.handle_verse_block(w, cont, args)?, + DynBlockBeg { name, args } => h.handle_dyn_block_beg(w, name, args)?, + DynBlockEnd => h.handle_dyn_block_end(w)?, + ListBeg { ordered } => h.handle_list_beg(w, ordered)?, + ListEnd { ordered } => h.handle_list_end(w, ordered)?, + ListItemBeg { bullet } => h.handle_list_beg_item(w, bullet)?, + ListItemEnd => h.handle_list_end_item(w)?, + Call { value } => h.handle_call(w, value)?, + Clock => h.handle_clock(w)?, + Comment(c) => h.handle_comment(w, c)?, + FixedWidth(f) => h.handle_fixed_width(w, f)?, + TableStart => h.handle_table_start(w)?, + TableEnd => h.handle_table_end(w)?, + TableCell => h.handle_table_cell(w)?, + LatexEnv => h.handle_latex_env(w)?, + FnDef { label, cont } => h.handle_fn_def(w, label, cont)?, + Keyword { key, value } => h.handle_keyword(w, key, value)?, + Rule => h.handle_rule(w)?, + Cookie(cookie) => h.handle_cookie(w, cookie)?, + FnRef { label, def } => h.handle_fn_ref(w, label, def)?, + InlineSrc { lang, option, body } => { + h.handle_inline_src(w, lang, option, body)? + } + InlineCall { + name, + args, + inside_header, + end_header, + } => h.handle_inline_call(w, name, args, inside_header, end_header)?, + Link { path, desc } => h.handle_link(w, path, desc)?, + Macros { name, args } => h.handle_macros(w, name, args)?, + RadioTarget { target } => h.handle_radio_target(w, target)?, + Snippet { name, value } => h.handle_snippet(w, name, value)?, + Target { target } => h.handle_target(w, target)?, + BoldBeg => h.handle_bold_beg(w)?, + BoldEnd => h.handle_bold_end(w)?, + ItalicBeg => h.handle_italic_beg(w)?, + ItalicEnd => h.handle_italic_end(w)?, + StrikeBeg => h.handle_strike_beg(w)?, + StrikeEnd => h.handle_strike_end(w)?, + UnderlineBeg => h.handle_underline_beg(w)?, + UnderlineEnd => h.handle_underline_end(w)?, + Verbatim(cont) => h.handle_verbatim(w, cont)?, + Code(cont) => h.handle_code(w, cont)?, + Text(cont) => h.handle_text(w, cont)?, + } + } + + Ok(()) + } + } + }; } + +create_render!( + HtmlHandler, + DefaultHtmlHandller, + HtmlRender, + DefaultHtmlRender +); diff --git a/src/lib.rs b/src/lib.rs index 06c1e90..900066d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,52 +5,89 @@ //! ```rust //! use orgize::Parser; //! -//! fn main() { -//! let parser = Parser::new( -//! r#"* Title 1 +//! let parser = Parser::new( +//! r"* Title 1 //! *Section 1* //! ** Title 2 //! _Section 2_ //! * Title 3 //! /Section 3/ //! * Title 4 -//! =Section 4="#, +//! =Section 4=", //! ); //! -//! for event in parser { -//! // handling the event -//! } +//! for event in parser { +//! // handling the event //! } //! ``` //! -//! Alternatively, you can use the built-in render. +//! Alternatively, you can use the built-in render directly: //! //! ```rust -//! use orgize::export::{HtmlHandler, Render}; +//! use orgize::export::DefaultHtmlRender; //! use std::io::Cursor; //! -//! fn main() { -//! let contents = r#"* Title 1 +//! let contents = r"* Title 1 //! *Section 1* //! ** Title 2 //! _Section 2_ //! * Title 3 //! /Section 3/ //! * Title 4 -//! =Section 4="#; +//! =Section 4="; //! -//! let cursor = Cursor::new(Vec::new()); -//! let mut render = Render::new(HtmlHandler, cursor, &contents); +//! let cursor = Cursor::new(Vec::new()); +//! let mut render = DefaultHtmlRender::new(cursor, &contents); //! -//! render -//! .render() -//! .expect("something went wrong rendering the file"); +//! render +//! .render() +//! .expect("something went wrong rendering the file"); //! -//! println!( -//! "{}", -//! String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8") -//! ); +//! let result = String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8"); +//! ``` +//! +//! or `impl HtmlHandler` to create your own render. The following example +//! add an anchor to every headline. +//! +//! ```rust +//! use std::io::{Cursor, Result, Write}; +//! +//! use orgize::export::*; +//! use orgize::headline::Headline; +//! use slugify::slugify; +//! +//! struct CustomHtmlHandler; +//! +//! impl HtmlHandler for CustomHtmlHandler { +//! fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()> { +//! write!( +//! w, +//! r##"{2}"##, +//! if hdl.level <= 6 { hdl.level } else { 6 }, +//! slugify!(hdl.title), +//! hdl.title, +//! ) +//! } //! } +//! +//! let contents = r"* Title 1 +//! *Section 1* +//! ** Title 2 +//! _Section 2_ +//! * Title 3 +//! /Section 3/ +//! * Title 4 +//! =Section 4="; +//! +//! let cursor = Cursor::new(Vec::new()); +//! +//! let mut render = HtmlRender::new(CustomHtmlHandler, cursor, &contents); +//! +//! render +//! .render() +//! .expect("something went wrong rendering the file"); +//! +//! let result = String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8"); //! ``` #[macro_use]