refactor(export): HtmlRender & HtmlHandler

This commit is contained in:
PoiScript 2019-02-14 13:38:48 +08:00
parent 8ba9ade62d
commit 69de17ad9b
7 changed files with 246 additions and 237 deletions

View file

@ -16,3 +16,6 @@ travis-ci = { repository = "PoiScript/orgize" }
bytecount = "0.5.1" bytecount = "0.5.1"
jetscii = "0.4.3" jetscii = "0.4.3"
memchr = "2" memchr = "2"
[dev-dependencies]
slugify = "0.1.0"

View file

@ -23,52 +23,45 @@ extern crate orgize;
```rust ```rust
use orgize::Parser; use orgize::Parser;
fn main() { let parser = Parser::new(
let parser = Parser::new( r"* Title 1
r#"* Title 1
*Section 1* *Section 1*
** Title 2 ** Title 2
_Section 2_ _Section 2_
* Title 3 * Title 3
/Section 3/ /Section 3/
* Title 4 * Title 4
=Section 4="#, =Section 4=",
); );
for event in parser { for event in parser {
// handling the event // handling the event
}
} }
``` ```
Alternatively, you can use the built-in render. Alternatively, you can use the built-in render.
```rust ```rust
use orgize::export::{HtmlHandler, Render}; use orgize::export::DefaultHtmlRender;
use std::io::Cursor; use std::io::Cursor;
fn main() { let contents = r"* Title 1
let contents = r#"* Title 1
*Section 1* *Section 1*
** Title 2 ** Title 2
_Section 2_ _Section 2_
* Title 3 * Title 3
/Section 3/ /Section 3/
* Title 4 * Title 4
=Section 4="#; =Section 4=";
let cursor = Cursor::new(Vec::new()); let cursor = Cursor::new(Vec::new());
let mut render = Render::new(HtmlHandler, cursor, &contents); let mut render = DefaultHtmlRender::new(cursor, &contents);
render render
.render() .render()
.expect("something went wrong rendering the file"); .expect("something went wrong rendering the file");
println!( let result = String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8");
"{}",
String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8")
);
}
``` ```
## License ## License

View file

@ -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<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: {} <org-file>", 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")
);
}

View file

@ -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<W: Write> HtmlHandler<W> for CustomHtmlHandler {
fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()> {
write!(
w,
r##"<h{0}><a class="anchor" href="#{1}">{2}</a></h{0}>"##,
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: {} <org-file>", 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(())
}

View file

@ -1,14 +1,11 @@
#![allow(unused_variables)] #![allow(unused_variables)]
use crate::elements::Key; use crate::elements::Key;
use crate::export::Handler;
use crate::headline::Headline; use crate::headline::Headline;
use crate::objects::Cookie; use crate::objects::Cookie;
use std::io::{Result, Write}; use std::io::{Result, Write};
pub struct HtmlHandler; pub trait HtmlHandler<W: Write> {
impl<W: Write> Handler<W> for HtmlHandler {
fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()> { fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()> {
let level = if hdl.level <= 6 { hdl.level } else { 6 }; let level = if hdl.level <= 6 { hdl.level } else { 6 };
write!(w, "<h{0}>{1}</h{0}>", level, hdl.title) write!(w, "<h{0}>{1}</h{0}>", level, hdl.title)

View file

@ -2,171 +2,134 @@ mod html;
pub use self::html::HtmlHandler; pub use self::html::HtmlHandler;
use crate::elements::Key;
use crate::headline::Headline;
use crate::objects::Cookie;
use crate::parser::Parser; use crate::parser::Parser;
use std::io::{Result, Write}; use std::io::{Result, Write};
pub trait Handler<W: Write> { macro_rules! create_render {
fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()>; ($handler:ident, $default_handler:ident, $render:ident, $default_render:ident) => {
fn handle_headline_end(&mut self, w: &mut W) -> Result<()>; struct $default_handler;
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<()>;
}
pub struct Render<'a, W: Write, H: Handler<W>> { impl<W: Write> $handler<W> for $default_handler {}
pub parser: Parser<'a>,
pub handler: H,
writer: W,
}
impl<'a, W: Write, H: Handler<W>> Render<'a, W, H> { pub struct $default_render<'a, W: Write>($render<'a, W, $default_handler>);
pub fn new(handler: H, writer: W, text: &'a str) -> Render<'a, W, H> {
Render {
parser: Parser::new(text),
handler,
writer,
}
}
pub fn into_wirter(self) -> W { impl<'a, W: Write> $default_render<'a, W> {
self.writer #[inline]
} pub fn new(writer: W, text: &'a str) -> Self {
$default_render($render::new($default_handler, writer, text))
}
pub fn render(&mut self) -> Result<()> { #[inline]
use crate::parser::Event::*; pub fn into_wirter(self) -> W {
self.0.writer
}
let w = &mut self.writer; #[inline]
let h = &mut self.handler; pub fn render(&mut self) -> Result<()> {
self.0.render()
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(()) pub struct $render<'a, W: Write, H: $handler<W>> {
} pub parser: Parser<'a>,
handler: H,
writer: W,
}
impl<'a, W: Write, H: $handler<W>> $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
);

View file

@ -5,52 +5,89 @@
//! ```rust //! ```rust
//! use orgize::Parser; //! use orgize::Parser;
//! //!
//! fn main() { //! let parser = Parser::new(
//! let parser = Parser::new( //! r"* Title 1
//! r#"* Title 1
//! *Section 1* //! *Section 1*
//! ** Title 2 //! ** Title 2
//! _Section 2_ //! _Section 2_
//! * Title 3 //! * Title 3
//! /Section 3/ //! /Section 3/
//! * Title 4 //! * Title 4
//! =Section 4="#, //! =Section 4=",
//! ); //! );
//! //!
//! for event in parser { //! for event in parser {
//! // handling the event //! // handling the event
//! }
//! } //! }
//! ``` //! ```
//! //!
//! Alternatively, you can use the built-in render. //! Alternatively, you can use the built-in render directly:
//! //!
//! ```rust //! ```rust
//! use orgize::export::{HtmlHandler, Render}; //! use orgize::export::DefaultHtmlRender;
//! use std::io::Cursor; //! use std::io::Cursor;
//! //!
//! fn main() { //! let contents = r"* Title 1
//! let contents = r#"* Title 1
//! *Section 1* //! *Section 1*
//! ** Title 2 //! ** Title 2
//! _Section 2_ //! _Section 2_
//! * Title 3 //! * Title 3
//! /Section 3/ //! /Section 3/
//! * Title 4 //! * Title 4
//! =Section 4="#; //! =Section 4=";
//! //!
//! let cursor = Cursor::new(Vec::new()); //! let cursor = Cursor::new(Vec::new());
//! let mut render = Render::new(HtmlHandler, cursor, &contents); //! let mut render = DefaultHtmlRender::new(cursor, &contents);
//! //!
//! render //! render
//! .render() //! .render()
//! .expect("something went wrong rendering the file"); //! .expect("something went wrong rendering the file");
//! //!
//! println!( //! let result = String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8");
//! "{}", //! ```
//! 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<W: Write> HtmlHandler<W> for CustomHtmlHandler {
//! fn handle_headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<()> {
//! write!(
//! w,
//! r##"<h{0}><a class="anchor" href="#{1}">{2}</a></h{0}>"##,
//! 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] #[macro_use]