From 0a876e2f2b8ed2e2c1d864297cfb776bd9c7a050 Mon Sep 17 00:00:00 2001 From: PoiScript Date: Thu, 27 Jun 2019 01:53:50 +0800 Subject: [PATCH] feat(export): update html render --- examples/custom_handler.rs | 82 +++++----- src/elements/keyword.rs | 6 + src/elements/mod.rs | 4 +- src/export/html.rs | 311 +++++++++++-------------------------- src/export/mod.rs | 68 -------- src/iter.rs | 10 +- src/lib.rs | 2 + src/org.rs | 60 +++++-- src/serde.rs | 2 +- tests/html.rs | 26 ++-- 10 files changed, 213 insertions(+), 358 deletions(-) diff --git a/examples/custom_handler.rs b/examples/custom_handler.rs index 46091e0..807919a 100644 --- a/examples/custom_handler.rs +++ b/examples/custom_handler.rs @@ -1,72 +1,72 @@ -use orgize::export::*; -use orgize::headline::Headline; -use slugify::slugify; use std::convert::From; use std::env::args; -use std::fs::File; -use std::io::{Cursor, Error as IOError, Read, Write}; +use std::fs; +use std::io::{Error as IOError, Write}; +use std::result::Result; use std::string::FromUtf8Error; -struct CustomHtmlHandler; +use orgize::export::*; +use orgize::{Container, Org}; +use slugify::slugify; #[derive(Debug)] -enum Error { +enum MyError { IO(IOError), Heading, Utf8(FromUtf8Error), } -// From trait is required -impl From for Error { - fn from(err: IOError) -> Error { - Error::IO(err) +// From trait is required for custom error type +impl From for MyError { + fn from(err: IOError) -> Self { + MyError::IO(err) } } -impl From for Error { - fn from(err: FromUtf8Error) -> Error { - Error::Utf8(err) +impl From for MyError { + fn from(err: FromUtf8Error) -> Self { + MyError::Utf8(err) } } -type Result = std::result::Result<(), Error>; +struct CustomHtmlHandler; -impl HtmlHandler for CustomHtmlHandler { - fn headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result { - if hdl.level > 6 { - Err(Error::Heading) - } else { - Ok(write!( - w, - r##"{2}"##, - hdl.level, - slugify!(hdl.title), - hdl.title, - )?) +impl HtmlHandler for CustomHtmlHandler { + fn start(&mut self, mut w: W, container: Container<'_>) -> Result<(), MyError> { + let mut default_handler = DefaultHtmlHandler; + match container { + Container::Headline(hdl) => { + if hdl.level > 6 { + return Err(MyError::Heading); + } else { + let slugify = slugify!(hdl.title); + write!( + w, + "{2}", + hdl.level, slugify, hdl.title, + )?; + } + } + _ => default_handler.start(w, container)?, } + Ok(()) } } -fn main() -> Result { +fn main() -> Result<(), MyError> { let args: Vec<_> = args().collect(); if args.len() < 2 { - println!("Usage: {} ", args[0]); + eprintln!("Usage: {} ", args[0]); } else { - let mut file = File::open(&args[1])?; + let contents = String::from_utf8(fs::read(&args[1])?)?; + let mut org = Org::new(&contents); + let mut writer = Vec::new(); - let mut contents = String::new(); - file.read_to_string(&mut contents)?; + org.parse(); + org.html(&mut writer, CustomHtmlHandler)?; - let mut 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, &mut cursor, &contents); - - render.render()?; - - println!("{}", String::from_utf8(cursor.into_inner())?); + println!("{}", String::from_utf8(writer)?); } Ok(()) diff --git a/src/elements/keyword.rs b/src/elements/keyword.rs index 153043a..bb692a5 100644 --- a/src/elements/keyword.rs +++ b/src/elements/keyword.rs @@ -8,6 +8,12 @@ pub struct Keyword<'a> { pub value: &'a str, } +#[derive(Debug)] +pub struct BabelCall<'a> { + pub key: &'a str, + pub value: &'a str, +} + impl Keyword<'_> { #[inline] // return (key, option, value, offset) diff --git a/src/elements/mod.rs b/src/elements/mod.rs index df02608..d75a7c8 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -33,7 +33,7 @@ pub use self::{ headline::Headline, inline_call::InlineCall, inline_src::InlineSrc, - keyword::Keyword, + keyword::{BabelCall, Keyword}, link::Link, list::{List, ListItem}, macros::Macros, @@ -56,7 +56,7 @@ pub enum Element<'a> { contents_end: usize, }, BabelCall { - value: &'a str, + call: BabelCall<'a>, begin: usize, end: usize, }, diff --git a/src/export/html.rs b/src/export/html.rs index b23e79f..faed3d1 100644 --- a/src/export/html.rs +++ b/src/export/html.rs @@ -1,15 +1,13 @@ #![allow(unused_variables)] +#![allow(unused_mut)] -use crate::{elements::*, headline::Headline, objects::*, Event, Parser}; +use crate::elements::*; +use crate::iter::Container; use jetscii::bytes; -use std::{ - convert::From, - io::{Error, Write}, - marker::PhantomData, -}; +use std::io::{Error, Write}; -pub trait HtmlHandler> { - fn escape(&mut self, w: &mut W, text: &str) -> Result<(), E> { +pub trait HtmlHandler> { + fn escape(&mut self, mut w: W, text: &str) -> Result<(), E> { let mut pos = 0; let bytes = text.as_bytes(); while let Some(off) = bytes!(b'<', b'>', b'&', b'\'', b'"').find(&bytes[pos..]) { @@ -29,261 +27,138 @@ pub trait HtmlHandler> { Ok(w.write_all(&bytes[pos..])?) } - fn event(&mut self, w: &mut W, event: Event) -> Result<(), E> { - handle_event!(event, self, w); - Ok(()) - } - fn headline_beg(&mut self, w: &mut W, hdl: Headline) -> Result<(), E> { - let level = if hdl.level <= 6 { hdl.level } else { 6 }; - write!(w, "", level)?; - self.text(w, hdl.title)?; - write!(w, "", level)?; - Ok(()) - } - fn headline_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(()) - } - fn section_beg(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "
")?) - } - fn section_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "
")?) - } - fn drawer_beg(&mut self, w: &mut W, name: &str) -> Result<(), E> { - Ok(()) - } - fn drawer_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(()) - } - fn paragraph_beg(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "

")?) - } - fn paragraph_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "

")?) - } - fn ctr_block_beg(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, r#"
"#)?) - } - fn ctr_block_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "
")?) - } - fn qte_block_beg(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "
")?) - } - fn qte_block_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "
")?) - } - fn spl_block_beg(&mut self, w: &mut W, name: &str, args: Option<&str>) -> Result<(), E> { - Ok(write!(w, "
")?) - } - fn spl_block_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "
")?) - } - fn comment_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> { - Ok(()) - } - fn example_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> { - write!(w, "
")?;
-        self.escape(w, cont)?;
-        write!(w, "
")?; - Ok(()) - } - fn export_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> { - Ok(()) - } - fn src_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> { - write!(w, "
")?;
-        self.escape(w, cont)?;
-        write!(w, "
")?; - Ok(()) - } - fn verse_block(&mut self, w: &mut W, cont: &str, args: Option<&str>) -> Result<(), E> { - Ok(()) - } - fn dyn_block_beg(&mut self, w: &mut W, name: &str, args: Option<&str>) -> Result<(), E> { - Ok(()) - } - fn dyn_block_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(()) - } - fn list_beg(&mut self, w: &mut W, _indent: usize, ordered: bool) -> Result<(), E> { - if ordered { - Ok(write!(w, "
    ")?) - } else { - Ok(write!(w, "
      ")?) - } - } - fn list_end(&mut self, w: &mut W, _indent: usize, ordered: bool) -> Result<(), E> { - if ordered { - Ok(write!(w, "
")?) - } else { - Ok(write!(w, "")?) - } - } - fn list_beg_item(&mut self, w: &mut W, bullet: &str) -> Result<(), E> { - Ok(write!(w, "
  • ")?) - } - fn list_end_item(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "
  • ")?) - } - fn call(&mut self, w: &mut W, value: &str) -> Result<(), E> { - Ok(()) - } - fn clock(&mut self, w: &mut W, clock: Clock<'_>) -> Result<(), E> { - Ok(()) - } - fn comment(&mut self, w: &mut W, cont: &str) -> Result<(), E> { - Ok(()) - } - fn fixed_width(&mut self, w: &mut W, cont: &str) -> Result<(), E> { - for line in cont.lines() { - // remove leading colon - write!(w, "
    ")?;
    -            self.escape(w, &line[1..])?;
    -            write!(w, "
    ")?; + fn start(&mut self, mut w: W, container: Container) -> Result<(), E> { + match container { + Container::Block(block) => write!(w, "
    ")?, + Container::Bold => write!(w, "")?, + Container::Document => write!(w, "
    ")?, + Container::DynBlock(_) => (), + Container::Headline(hdl) => { + let level = if hdl.level <= 6 { hdl.level } else { 6 }; + write!(&mut w, "", level)?; + self.text(&mut w, hdl.title)?; + write!(&mut w, "", level)?; + } + Container::Italic => write!(w, "")?, + Container::List(list) => { + if list.ordered { + write!(w, "
      ")?; + } else { + write!(w, "
        ")?; + } + } + Container::ListItem(_) => write!(w, "
      • ")?, + Container::Paragraph => write!(w, "

        ")?, + Container::Section => write!(w, "

        ")?, + Container::Strike => write!(w, "")?, + Container::Underline => write!(w, "")?, } Ok(()) } - fn table_start(&mut self, w: &mut W) -> Result<(), E> { + fn end(&mut self, mut w: W, container: Container) -> Result<(), E> { + match container { + Container::Block(block) => write!(w, "
    ")?, + Container::Bold => write!(w, "")?, + Container::Document => write!(w, "")?, + Container::DynBlock(_) => (), + Container::Headline(_) => (), + Container::Italic => write!(w, "")?, + Container::List(list) => { + if list.ordered { + write!(w, "")?; + } else { + write!(w, "")?; + } + } + Container::ListItem(_) => write!(w, "")?, + Container::Paragraph => write!(w, "

    ")?, + Container::Section => write!(w, "")?, + Container::Strike => write!(w, "
    ")?, + Container::Underline => write!(w, "")?, + } Ok(()) } - fn table_end(&mut self, w: &mut W) -> Result<(), E> { + fn keyword(&mut self, mut w: W, keyword: &Keyword<'_>) -> Result<(), E> { Ok(()) } - fn table_cell(&mut self, w: &mut W) -> Result<(), E> { + fn drawer(&mut self, mut w: W, drawer: &Drawer<'_>) -> Result<(), E> { Ok(()) } - fn latex_env(&mut self, w: &mut W) -> Result<(), E> { - Ok(()) - } - fn fn_def(&mut self, w: &mut W, label: &str, cont: &str) -> Result<(), E> { - Ok(()) - } - fn keyword(&mut self, w: &mut W, keyword: Keyword<'_>) -> Result<(), E> { - Ok(()) - } - fn rule(&mut self, w: &mut W) -> Result<(), E> { + fn rule(&mut self, mut w: W) -> Result<(), E> { Ok(write!(w, "
    ")?) } - fn cookie(&mut self, w: &mut W, cookie: Cookie) -> Result<(), E> { + fn cookie(&mut self, mut w: W, cookie: &Cookie) -> Result<(), E> { Ok(()) } - fn fn_ref(&mut self, w: &mut W, fn_ref: FnRef<'_>) -> Result<(), E> { + fn fn_ref(&mut self, mut w: W, fn_ref: &FnRef<'_>) -> Result<(), E> { Ok(()) } - fn inline_call(&mut self, w: &mut W, call: InlineCall<'_>) -> Result<(), E> { + fn babel_call(&mut self, mut w: W, call: &BabelCall<'_>) -> Result<(), E> { Ok(()) } - fn inline_src(&mut self, w: &mut W, src: InlineSrc<'_>) -> Result<(), E> { - write!(w, "")?; - self.text(w, src.body)?; - write!(w, "")?; + fn inline_call(&mut self, mut w: W, call: &InlineCall<'_>) -> Result<(), E> { Ok(()) } - fn link(&mut self, w: &mut W, link: Link<'_>) -> Result<(), E> { - write!(w, r#""#)?; - self.text(w, link.desc.unwrap_or(link.path))?; - write!(w, "")?; + fn inline_src(&mut self, mut w: W, src: &InlineSrc<'_>) -> Result<(), E> { + write!(&mut w, "")?; + self.text(&mut w, src.body)?; + write!(&mut w, "")?; Ok(()) } - fn macros(&mut self, w: &mut W, macros: Macros<'_>) -> Result<(), E> { + fn link(&mut self, mut w: W, link: &Link<'_>) -> Result<(), E> { + write!(&mut w, r#""#)?; + self.text(&mut w, link.desc.unwrap_or(link.path))?; + write!(&mut w, "")?; Ok(()) } - fn radio_target(&mut self, w: &mut W, target: &str) -> Result<(), E> { + fn macros(&mut self, mut w: W, macros: &Macros<'_>) -> Result<(), E> { Ok(()) } - fn snippet(&mut self, w: &mut W, snippet: Snippet<'_>) -> Result<(), E> { + fn radio_target(&mut self, mut w: W, target: &RadioTarget<'_>) -> Result<(), E> { + Ok(()) + } + fn snippet(&mut self, mut w: W, snippet: &Snippet<'_>) -> Result<(), E> { if snippet.name.eq_ignore_ascii_case("HTML") { Ok(write!(w, "{}", snippet.value)?) } else { Ok(()) } } - fn target(&mut self, w: &mut W, target: &str) -> Result<(), E> { + fn target(&mut self, mut w: W, target: &Target<'_>) -> Result<(), E> { Ok(()) } - fn timestamp(&mut self, w: &mut W, timestamp: Timestamp) -> Result<(), E> { + fn timestamp(&mut self, mut w: W, timestamp: &Timestamp) -> Result<(), E> { Ok(()) } - fn bold_beg(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "")?) - } - fn bold_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "")?) - } - fn italic_beg(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "")?) - } - fn italic_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "")?) - } - fn strike_beg(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "")?) - } - fn strike_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "")?) - } - fn underline_beg(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "")?) - } - fn underline_end(&mut self, w: &mut W) -> Result<(), E> { - Ok(write!(w, "")?) - } - fn verbatim(&mut self, w: &mut W, cont: &str) -> Result<(), E> { - write!(w, "")?; - self.text(w, cont)?; - write!(w, "")?; + fn verbatim(&mut self, mut w: W, cont: &str) -> Result<(), E> { + write!(&mut w, "")?; + self.text(&mut w, cont)?; + write!(&mut w, "")?; Ok(()) } - fn code(&mut self, w: &mut W, cont: &str) -> Result<(), E> { - write!(w, "")?; - self.text(w, cont)?; - write!(w, "")?; + fn code(&mut self, mut w: W, cont: &str) -> Result<(), E> { + write!(&mut w, "")?; + self.text(&mut w, cont)?; + write!(&mut w, "")?; Ok(()) } - fn text(&mut self, w: &mut W, cont: &str) -> Result<(), E> { + fn text(&mut self, mut w: W, cont: &str) -> Result<(), E> { self.escape(w, cont)?; Ok(()) } - fn planning(&mut self, w: &mut W, planning: Planning) -> Result<(), E> { + fn planning(&mut self, mut w: W, planning: &Planning) -> Result<(), E> { + Ok(()) + } + fn clock(&mut self, mut w: W, clock: &Clock<'_>) -> Result<(), E> { + Ok(()) + } + fn fn_def(&mut self, mut w: W, fn_def: &FnDef<'_>) -> Result<(), E> { Ok(()) } } pub struct DefaultHtmlHandler; -impl HtmlHandler for DefaultHtmlHandler {} - -pub struct HtmlRender<'a, W: Write, E: From, H: HtmlHandler> { - pub parser: Parser<'a>, - pub writer: &'a mut W, - handler: H, - error_type: PhantomData, -} - -impl<'a, W: Write> HtmlRender<'a, W, Error, DefaultHtmlHandler> { - pub fn default(writer: &'a mut W, text: &'a str) -> Self { - HtmlRender::new(DefaultHtmlHandler, writer, text) - } -} - -impl<'a, W: Write, E: From, H: HtmlHandler> HtmlRender<'a, W, E, H> { - pub fn new(handler: H, writer: &'a mut W, text: &'a str) -> Self { - HtmlRender { - parser: Parser::new(text), - handler, - writer, - error_type: PhantomData, - } - } - - pub fn render(&mut self) -> Result<(), E> { - for event in &mut self.parser { - self.handler.event(self.writer, event)?; - } - - Ok(()) - } -} +impl HtmlHandler for DefaultHtmlHandler {} diff --git a/src/export/mod.rs b/src/export/mod.rs index d747c6e..4a0a220 100644 --- a/src/export/mod.rs +++ b/src/export/mod.rs @@ -1,71 +1,3 @@ -#[macro_use] -macro_rules! handle_event { - ($event:expr, $handler:expr, $writer:expr) => { - use crate::parser::Event::*; - - match $event { - HeadlineBeg(hdl) => $handler.headline_beg($writer, hdl)?, - HeadlineEnd => $handler.headline_end($writer)?, - SectionBeg => $handler.section_beg($writer)?, - SectionEnd => $handler.section_end($writer)?, - ParagraphBeg => $handler.paragraph_beg($writer)?, - ParagraphEnd => $handler.paragraph_end($writer)?, - DrawerBeg(n) => $handler.drawer_beg($writer, n)?, - DrawerEnd => $handler.drawer_end($writer)?, - CtrBlockBeg => $handler.ctr_block_beg($writer)?, - CtrBlockEnd => $handler.ctr_block_end($writer)?, - QteBlockBeg => $handler.qte_block_beg($writer)?, - QteBlockEnd => $handler.qte_block_end($writer)?, - SplBlockBeg { name, args } => $handler.spl_block_beg($writer, name, args)?, - SplBlockEnd => $handler.spl_block_end($writer)?, - CommentBlock { cont, args } => $handler.comment_block($writer, cont, args)?, - ExampleBlock { cont, args } => $handler.example_block($writer, cont, args)?, - ExportBlock { cont, args } => $handler.export_block($writer, cont, args)?, - SrcBlock { cont, args } => $handler.src_block($writer, cont, args)?, - VerseBlock { cont, args } => $handler.verse_block($writer, cont, args)?, - DynBlockBeg { name, args } => $handler.dyn_block_beg($writer, name, args)?, - DynBlockEnd => $handler.dyn_block_end($writer)?, - ListBeg { indent, ordered } => $handler.list_beg($writer, indent, ordered)?, - ListEnd { indent, ordered } => $handler.list_end($writer, indent, ordered)?, - ListItemBeg { bullet } => $handler.list_beg_item($writer, bullet)?, - ListItemEnd => $handler.list_end_item($writer)?, - Call { value } => $handler.call($writer, value)?, - Planning(p) => $handler.planning($writer, p)?, - Clock(c) => $handler.clock($writer, c)?, - Timestamp(t) => $handler.timestamp($writer, t)?, - Comment(c) => $handler.comment($writer, c)?, - FixedWidth(f) => $handler.fixed_width($writer, f)?, - TableStart => $handler.table_start($writer)?, - TableEnd => $handler.table_end($writer)?, - TableCell => $handler.table_cell($writer)?, - LatexEnv => $handler.latex_env($writer)?, - FnDef { label, cont } => $handler.fn_def($writer, label, cont)?, - Keyword(keyword) => $handler.keyword($writer, keyword)?, - Rule => $handler.rule($writer)?, - Cookie(cookie) => $handler.cookie($writer, cookie)?, - FnRef(fn_ref) => $handler.fn_ref($writer, fn_ref)?, - InlineSrc(src) => $handler.inline_src($writer, src)?, - InlineCall(call) => $handler.inline_call($writer, call)?, - Link(link) => $handler.link($writer, link)?, - Macros(macros) => $handler.macros($writer, macros)?, - RadioTarget { target } => $handler.radio_target($writer, target)?, - Snippet(snippet) => $handler.snippet($writer, snippet)?, - Target { target } => $handler.target($writer, target)?, - BoldBeg => $handler.bold_beg($writer)?, - BoldEnd => $handler.bold_end($writer)?, - ItalicBeg => $handler.italic_beg($writer)?, - ItalicEnd => $handler.italic_end($writer)?, - StrikeBeg => $handler.strike_beg($writer)?, - StrikeEnd => $handler.strike_end($writer)?, - UnderlineBeg => $handler.underline_beg($writer)?, - UnderlineEnd => $handler.underline_end($writer)?, - Verbatim(cont) => $handler.verbatim($writer, cont)?, - Code(cont) => $handler.code($writer, cont)?, - Text(cont) => $handler.text($writer, cont)?, - } - }; -} - mod html; pub use html::*; diff --git a/src/iter.rs b/src/iter.rs index 7e9eb62..932f478 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -41,7 +41,7 @@ pub enum Event<'a> { Text(&'a str), Code(&'a str), Verbatim(&'a str), - BabelCall(&'a str), + BabelCall(&'a BabelCall<'a>), } enum State { @@ -73,9 +73,9 @@ impl<'a> Iter<'a> { self.state = State::Finished; None } - Element::BabelCall { value, .. } => { + Element::BabelCall { call, .. } => { self.state = State::Start; - Some(Event::BabelCall(value)) + Some(Event::BabelCall(call)) } Element::Verbatim { value, .. } => { self.state = State::Start; @@ -260,9 +260,9 @@ impl<'a> Iter<'a> { self.state = State::Finished; None } - Element::BabelCall { value, .. } => { + Element::BabelCall { call, .. } => { self.state = State::End; - Some(Event::BabelCall(value)) + Some(Event::BabelCall(call)) } Element::Verbatim { value, .. } => { self.state = State::End; diff --git a/src/lib.rs b/src/lib.rs index 213dedb..71e9f85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,9 +140,11 @@ //! ``` pub mod elements; +pub mod export; pub mod iter; pub mod org; #[cfg(feature = "serde")] mod serde; +pub use iter::{Container, Event}; pub use org::Org; diff --git a/src/org.rs b/src/org.rs index 74e8335..611891e 100644 --- a/src/org.rs +++ b/src/org.rs @@ -1,28 +1,32 @@ use crate::elements::*; +use crate::export::{DefaultHtmlHandler, HtmlHandler}; use crate::iter::Iter; use indextree::{Arena, NodeId}; use jetscii::bytes; use memchr::{memchr_iter, memrchr_iter}; +use std::io::{Error, Write}; pub struct Org<'a> { pub(crate) arena: Arena>, pub(crate) document: NodeId, - root: Option, + root: NodeId, text: &'a str, } impl<'a> Org<'a> { pub fn new(text: &'a str) -> Self { let mut arena = Arena::new(); + let root = arena.new_node(Element::Root); let document = arena.new_node(Element::Document { begin: 0, end: text.len(), }); + root.append(document, &mut arena).unwrap(); Org { arena, - root: None, + root, document, text, } @@ -32,14 +36,50 @@ impl<'a> Org<'a> { self.arena[self.document].first_child().is_some() } - pub fn iter(&'a mut self) -> Iter<'a> { - if let Some(root) = self.root { - Iter::new(&self.arena, root) - } else { - let root = self.arena.new_node(Element::Root); - root.append(self.document, &mut self.arena).unwrap(); - Iter::new(&self.arena, root) + pub fn iter(&'a self) -> Iter<'a> { + Iter::new(&self.arena, self.root) + } + + pub fn html(&self, mut writer: W, mut handler: H) -> Result<(), E> + where + W: Write, + E: From, + H: HtmlHandler, + { + use crate::iter::Event::*; + + for event in self.iter() { + match event { + Start(e) => handler.start(&mut writer, e)?, + End(e) => handler.end(&mut writer, e)?, + Clock(e) => handler.clock(&mut writer, e)?, + Cookie(e) => handler.cookie(&mut writer, e)?, + Drawer(e) => handler.drawer(&mut writer, e)?, + FnDef(e) => handler.fn_def(&mut writer, e)?, + FnRef(e) => handler.fn_ref(&mut writer, e)?, + InlineCall(e) => handler.inline_call(&mut writer, e)?, + InlineSrc(e) => handler.inline_src(&mut writer, e)?, + Keyword(e) => handler.keyword(&mut writer, e)?, + Link(e) => handler.link(&mut writer, e)?, + Macros(e) => handler.macros(&mut writer, e)?, + Planning(e) => handler.planning(&mut writer, e)?, + RadioTarget(e) => handler.radio_target(&mut writer, e)?, + Snippet(e) => handler.snippet(&mut writer, e)?, + Target(e) => handler.target(&mut writer, e)?, + Timestamp(e) => handler.timestamp(&mut writer, e)?, + Text(e) => handler.text(&mut writer, e)?, + Code(e) => handler.code(&mut writer, e)?, + Verbatim(e) => handler.verbatim(&mut writer, e)?, + BabelCall(e) => handler.babel_call(&mut writer, e)?, + Rule => handler.rule(&mut writer)?, + } } + + Ok(()) + } + + pub fn html_default(&self, wrtier: W) -> Result<(), Error> { + self.html(wrtier, DefaultHtmlHandler) } pub fn parse(&mut self) { @@ -327,7 +367,7 @@ impl<'a> Org<'a> { } else if let Some((key, option, value, end)) = Keyword::parse(tail) { if key.eq_ignore_ascii_case("CALL") { let call = Element::BabelCall { - value, + call: BabelCall { key, value }, begin, end: begin + line_begin + end, }; diff --git a/src/serde.rs b/src/serde.rs index 19131fe..f5fbd2a 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -201,7 +201,7 @@ impl Serialize for ElementNode<'_> { state.serialize_field("end", end)?; } } - Element::BabelCall { value, begin, end } => { + Element::BabelCall { call, begin, end } => { state = serializer.serialize_struct("Element::BabelCall", 2)?; state.serialize_field("type", "babel_call")?; if cfg!(feature = "extra-serde-info") { diff --git a/tests/html.rs b/tests/html.rs index 0d28dff..9b98f3a 100644 --- a/tests/html.rs +++ b/tests/html.rs @@ -1,17 +1,17 @@ extern crate orgize; -use orgize::export::HtmlRender; -use std::io::Cursor; +use orgize::Org; macro_rules! html_test { ($name:ident, $content:expr, $expected:expr) => { #[test] fn $name() { - let mut cursor = Cursor::new(Vec::new()); - let mut render = HtmlRender::default(&mut cursor, $content); - render.render().expect("render error"); - let s = String::from_utf8(cursor.into_inner()).expect("invalid utf-8"); - assert_eq!(s, $expected); + let mut writer = Vec::new(); + let mut org = Org::new($content); + org.parse(); + org.html_default(&mut writer).unwrap(); + let string = String::from_utf8(writer).unwrap(); + assert_eq!(string, $expected); } }; } @@ -19,7 +19,7 @@ macro_rules! html_test { html_test!( emphasis, "*bold*, /italic/,_underlined_, =verbatim= and ~code~", - "

    bold, italic,underlined, verbatim and code

    " + "

    bold, italic,underlined, verbatim and code

    " ); html_test!( @@ -32,14 +32,14 @@ _Section 2_ /Section 3/ * Title 4 =Section 4="#, - "

    Title 1

    \ + "

    Title 1

    \

    Section 1

    \

    Title 2

    \

    Section 2

    \

    Title 3

    \

    Section 3

    \

    Title 4

    \ -

    Section 4

    " +

    Section 4

    " ); html_test!( @@ -53,15 +53,15 @@ html_test!( - 4 + 5"#, - "
      \ + "
        \
      • 1

      • \
      • 2

        • 3

        • 4

      • \
      • 5

      • \ -
      " +
    " ); html_test!( snippet, "@@html:@@delete this@@html:@@", - "

    delete this

    " + "

    delete this

    " );