refactor(iter): use indextree::Traverse for iterating nodes
This commit is contained in:
parent
275fbfad34
commit
21aba13d71
|
@ -5,8 +5,8 @@ use std::io::{Error as IOError, Write};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
use orgize::export::*;
|
use orgize::export::{html::Escape, DefaultHtmlHandler, HtmlHandler};
|
||||||
use orgize::{Container, Org};
|
use orgize::{Element, Org};
|
||||||
use slugify::slugify;
|
use slugify::slugify;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -32,22 +32,25 @@ impl From<FromUtf8Error> for MyError {
|
||||||
struct MyHtmlHandler;
|
struct MyHtmlHandler;
|
||||||
|
|
||||||
impl HtmlHandler<MyError> for MyHtmlHandler {
|
impl HtmlHandler<MyError> for MyHtmlHandler {
|
||||||
fn start<W: Write>(&mut self, mut w: W, container: Container<'_>) -> Result<(), MyError> {
|
fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> {
|
||||||
let mut default_handler = DefaultHtmlHandler;
|
let mut default_handler = DefaultHtmlHandler;
|
||||||
match container {
|
match element {
|
||||||
Container::Headline(hdl) => {
|
Element::Headline { headline, .. } => {
|
||||||
if hdl.level > 6 {
|
if headline.level > 6 {
|
||||||
return Err(MyError::Heading);
|
return Err(MyError::Heading);
|
||||||
} else {
|
} else {
|
||||||
let slugify = slugify!(hdl.title);
|
let slugify = slugify!(headline.title);
|
||||||
write!(
|
write!(
|
||||||
w,
|
w,
|
||||||
"<h{0}><a id=\"{1}\" href=\"#{1}\">{2}</a></h{0}>",
|
"<h{0}><a id=\"{1}\" href=\"#{1}\">{2}</a></h{0}>",
|
||||||
hdl.level, slugify, hdl.title,
|
headline.level,
|
||||||
|
slugify,
|
||||||
|
Escape(headline.title),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => default_handler.start(w, container)?,
|
// fallthrough to default handler
|
||||||
|
_ => default_handler.start(w, element)?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,6 @@ use indextree::NodeId;
|
||||||
#[cfg_attr(feature = "serde", serde(tag = "type"))]
|
#[cfg_attr(feature = "serde", serde(tag = "type"))]
|
||||||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
|
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
|
||||||
pub enum Element<'a> {
|
pub enum Element<'a> {
|
||||||
Root,
|
|
||||||
Block {
|
Block {
|
||||||
#[cfg_attr(feature = "serde", serde(flatten))]
|
#[cfg_attr(feature = "serde", serde(flatten))]
|
||||||
block: Block<'a>,
|
block: Block<'a>,
|
||||||
|
|
|
@ -1,171 +1,127 @@
|
||||||
#![allow(unused_variables)]
|
use crate::elements::Element;
|
||||||
#![allow(unused_mut)]
|
|
||||||
|
|
||||||
use crate::elements::*;
|
|
||||||
use crate::iter::Container;
|
|
||||||
use jetscii::bytes;
|
use jetscii::bytes;
|
||||||
|
use std::fmt;
|
||||||
use std::io::{Error, Write};
|
use std::io::{Error, Write};
|
||||||
|
|
||||||
pub trait HtmlHandler<E: From<Error>> {
|
pub struct Escape<'a>(pub &'a str);
|
||||||
fn escape<W: Write>(&mut self, mut w: W, text: &str) -> Result<(), E> {
|
|
||||||
|
impl fmt::Display for Escape<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
let bytes = text.as_bytes();
|
let bytes = self.0.as_bytes();
|
||||||
while let Some(off) = bytes!(b'<', b'>', b'&', b'\'', b'"').find(&bytes[pos..]) {
|
while let Some(off) = bytes!(b'<', b'>', b'&', b'\'', b'"').find(&bytes[pos..]) {
|
||||||
w.write_all(&bytes[pos..pos + off])?;
|
write!(f, "{}", &self.0[pos..pos + off])?;
|
||||||
|
|
||||||
pos += off + 1;
|
pos += off + 1;
|
||||||
|
|
||||||
match text.as_bytes()[pos - 1] {
|
match bytes[pos - 1] {
|
||||||
b'<' => w.write_all(b"<")?,
|
b'<' => write!(f, "<")?,
|
||||||
b'>' => w.write_all(b">")?,
|
b'>' => write!(f, ">")?,
|
||||||
b'&' => w.write_all(b"&")?,
|
b'&' => write!(f, "&")?,
|
||||||
b'\'' => w.write_all(b"'")?,
|
b'\'' => write!(f, "'")?,
|
||||||
b'"' => w.write_all(b""")?,
|
b'"' => write!(f, """)?,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(w.write_all(&bytes[pos..])?)
|
write!(f, "{}", &self.0[pos..])
|
||||||
}
|
}
|
||||||
fn start<W: Write>(&mut self, mut w: W, container: Container) -> Result<(), E> {
|
}
|
||||||
match container {
|
|
||||||
Container::Block(block) => write!(w, "<div>")?,
|
pub trait HtmlHandler<E: From<Error>> {
|
||||||
Container::Bold => write!(w, "<b>")?,
|
fn start<W: Write>(&mut self, mut w: W, element: &Element) -> Result<(), E> {
|
||||||
Container::Document => write!(w, "<main>")?,
|
use Element::*;
|
||||||
Container::DynBlock(_) => (),
|
|
||||||
Container::Headline(hdl) => {
|
match element {
|
||||||
let level = if hdl.level <= 6 { hdl.level } else { 6 };
|
// container elements
|
||||||
write!(&mut w, "<h{}>", level)?;
|
Block { .. } => write!(w, "<div>")?,
|
||||||
self.text(&mut w, hdl.title)?;
|
Bold { .. } => write!(w, "<b>")?,
|
||||||
write!(&mut w, "</h{}>", level)?;
|
Document { .. } => write!(w, "<main>")?,
|
||||||
|
DynBlock { .. } => (),
|
||||||
|
Headline { headline, .. } => {
|
||||||
|
let level = if headline.level <= 6 {
|
||||||
|
headline.level
|
||||||
|
} else {
|
||||||
|
6
|
||||||
|
};
|
||||||
|
write!(w, "<h{0}>{1}</h{0}>", level, Escape(headline.title))?;
|
||||||
}
|
}
|
||||||
Container::Italic => write!(w, "<i>")?,
|
List { list, .. } => {
|
||||||
Container::List(list) => {
|
|
||||||
if list.ordered {
|
if list.ordered {
|
||||||
write!(w, "<ol>")?;
|
write!(w, "<ol>")?;
|
||||||
} else {
|
} else {
|
||||||
write!(w, "<ul>")?;
|
write!(w, "<ul>")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Container::ListItem(_) => write!(w, "<li>")?,
|
Italic { .. } => write!(w, "<i>")?,
|
||||||
Container::Paragraph => write!(w, "<p>")?,
|
ListItem { .. } => write!(w, "<li>")?,
|
||||||
Container::Section => write!(w, "<section>")?,
|
Paragraph { .. } => write!(w, "<p>")?,
|
||||||
Container::Strike => write!(w, "<s>")?,
|
Section { .. } => write!(w, "<section>")?,
|
||||||
Container::Underline => write!(w, "<u>")?,
|
Strike { .. } => write!(w, "<s>")?,
|
||||||
|
Underline { .. } => write!(w, "<u>")?,
|
||||||
|
// non-container elements
|
||||||
|
BabelCall { .. } => (),
|
||||||
|
InlineSrc { inline_src, .. } => write!(w, "<code>{}</code>", Escape(inline_src.body))?,
|
||||||
|
Code { value, .. } => write!(w, "<code>{}</code>", Escape(value))?,
|
||||||
|
FnRef { .. } => (),
|
||||||
|
InlineCall { .. } => (),
|
||||||
|
Link { link, .. } => write!(
|
||||||
|
w,
|
||||||
|
"<a href=\"{}\">{}</a>",
|
||||||
|
Escape(link.path),
|
||||||
|
Escape(link.desc.unwrap_or(link.path)),
|
||||||
|
)?,
|
||||||
|
Macros { .. } => (),
|
||||||
|
Planning { .. } => (),
|
||||||
|
RadioTarget { .. } => (),
|
||||||
|
Snippet { snippet, .. } => {
|
||||||
|
if snippet.name.eq_ignore_ascii_case("HTML") {
|
||||||
|
write!(w, "{}", snippet.value)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Target { .. } => (),
|
||||||
|
Text { value, .. } => write!(w, "{}", Escape(value))?,
|
||||||
|
Timestamp { .. } => (),
|
||||||
|
Verbatim { value, .. } => write!(&mut w, "<code>{}</code>", Escape(value))?,
|
||||||
|
FnDef { .. } => (),
|
||||||
|
Clock { .. } => (),
|
||||||
|
Comment { value, .. } => write!(w, "<!--\n{}\n-->", Escape(value))?,
|
||||||
|
FixedWidth { value, .. } => write!(w, "<pre>{}</pre>", Escape(value))?,
|
||||||
|
Keyword { .. } => (),
|
||||||
|
Drawer { .. } => (),
|
||||||
|
Rule { .. } => write!(w, "<hr>")?,
|
||||||
|
Cookie { .. } => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn end<W: Write>(&mut self, mut w: W, container: Container) -> Result<(), E> {
|
fn end<W: Write>(&mut self, mut w: W, element: &Element) -> Result<(), E> {
|
||||||
match container {
|
use Element::*;
|
||||||
Container::Block(block) => write!(w, "</div>")?,
|
|
||||||
Container::Bold => write!(w, "</b>")?,
|
match element {
|
||||||
Container::Document => write!(w, "</main>")?,
|
// container elements
|
||||||
Container::DynBlock(_) => (),
|
Block { .. } => write!(w, "</div>")?,
|
||||||
Container::Headline(_) => (),
|
Bold { .. } => write!(w, "</b>")?,
|
||||||
Container::Italic => write!(w, "</i>")?,
|
Document { .. } => write!(w, "</main>")?,
|
||||||
Container::List(list) => {
|
DynBlock { .. } => (),
|
||||||
|
Headline { .. } => (),
|
||||||
|
List { list, .. } => {
|
||||||
if list.ordered {
|
if list.ordered {
|
||||||
write!(w, "</ol>")?;
|
write!(w, "</ol>")?;
|
||||||
} else {
|
} else {
|
||||||
write!(w, "</ul>")?;
|
write!(w, "</ul>")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Container::ListItem(_) => write!(w, "</li>")?,
|
Italic { .. } => write!(w, "</i>")?,
|
||||||
Container::Paragraph => write!(w, "</p>")?,
|
ListItem { .. } => write!(w, "</li>")?,
|
||||||
Container::Section => write!(w, "</section>")?,
|
Paragraph { .. } => write!(w, "</p>")?,
|
||||||
Container::Strike => write!(w, "</s>")?,
|
Section { .. } => write!(w, "</section>")?,
|
||||||
Container::Underline => write!(w, "</u>")?,
|
Strike { .. } => write!(w, "</s>")?,
|
||||||
|
Underline { .. } => write!(w, "</u>")?,
|
||||||
|
// non-container elements
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn keyword<W: Write>(&mut self, mut w: W, keyword: &Keyword<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn drawer<W: Write>(&mut self, mut w: W, drawer: &Drawer<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn rule<W: Write>(&mut self, mut w: W) -> Result<(), E> {
|
|
||||||
Ok(write!(w, "<hr>")?)
|
|
||||||
}
|
|
||||||
fn cookie<W: Write>(&mut self, mut w: W, cookie: &Cookie) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn fn_ref<W: Write>(&mut self, mut w: W, fn_ref: &FnRef<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn babel_call<W: Write>(&mut self, mut w: W, call: &BabelCall<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn inline_call<W: Write>(&mut self, mut w: W, call: &InlineCall<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn inline_src<W: Write>(&mut self, mut w: W, src: &InlineSrc<'_>) -> Result<(), E> {
|
|
||||||
write!(&mut w, "<code>")?;
|
|
||||||
self.text(&mut w, src.body)?;
|
|
||||||
write!(&mut w, "</code>")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn link<W: Write>(&mut self, mut w: W, link: &Link<'_>) -> Result<(), E> {
|
|
||||||
write!(&mut w, r#"<a href=""#)?;
|
|
||||||
self.text(&mut w, link.path)?;
|
|
||||||
write!(&mut w, r#"">"#)?;
|
|
||||||
self.text(&mut w, link.desc.unwrap_or(link.path))?;
|
|
||||||
write!(&mut w, "</a>")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn macros<W: Write>(&mut self, mut w: W, macros: &Macros<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn radio_target<W: Write>(&mut self, mut w: W, target: &RadioTarget<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn snippet<W: Write>(&mut self, mut w: W, snippet: &Snippet<'_>) -> Result<(), E> {
|
|
||||||
if snippet.name.eq_ignore_ascii_case("HTML") {
|
|
||||||
write!(w, "{}", snippet.value)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn target<W: Write>(&mut self, mut w: W, target: &Target<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn timestamp<W: Write>(&mut self, mut w: W, timestamp: &Timestamp) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn verbatim<W: Write>(&mut self, mut w: W, cont: &str) -> Result<(), E> {
|
|
||||||
write!(&mut w, "<code>")?;
|
|
||||||
self.text(&mut w, cont)?;
|
|
||||||
write!(&mut w, "</code>")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn code<W: Write>(&mut self, mut w: W, cont: &str) -> Result<(), E> {
|
|
||||||
write!(&mut w, "<code>")?;
|
|
||||||
self.text(&mut w, cont)?;
|
|
||||||
write!(&mut w, "</code>")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn text<W: Write>(&mut self, mut w: W, cont: &str) -> Result<(), E> {
|
|
||||||
self.escape(w, cont)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn planning<W: Write>(&mut self, mut w: W, planning: &Planning) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn clock<W: Write>(&mut self, mut w: W, clock: &Clock<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn fn_def<W: Write>(&mut self, mut w: W, fn_def: &FnDef<'_>) -> Result<(), E> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn comment<W: Write>(&mut self, mut w: W, value: &str) -> Result<(), E> {
|
|
||||||
write!(&mut w, "<!--\n")?;
|
|
||||||
self.text(&mut w, value)?;
|
|
||||||
write!(&mut w, "\n-->")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn fixed_width<W: Write>(&mut self, mut w: W, value: &str) -> Result<(), E> {
|
|
||||||
write!(&mut w, "<pre>")?;
|
|
||||||
self.text(&mut w, value)?;
|
|
||||||
write!(&mut w, "</pre>")?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
mod html;
|
pub mod html;
|
||||||
|
|
||||||
pub use html::*;
|
pub use html::{DefaultHtmlHandler, HtmlHandler};
|
||||||
|
|
384
src/iter.rs
384
src/iter.rs
|
@ -1,389 +1,25 @@
|
||||||
use indextree::{Arena, NodeId};
|
use indextree::{Arena, NodeEdge, Traverse};
|
||||||
|
|
||||||
use crate::elements::*;
|
use crate::elements::Element;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Container<'a> {
|
|
||||||
Block(&'a Block<'a>),
|
|
||||||
Bold,
|
|
||||||
Document,
|
|
||||||
DynBlock(&'a DynBlock<'a>),
|
|
||||||
Headline(&'a Headline<'a>),
|
|
||||||
Italic,
|
|
||||||
List(&'a List),
|
|
||||||
ListItem(&'a ListItem<'a>),
|
|
||||||
Paragraph,
|
|
||||||
Section,
|
|
||||||
Strike,
|
|
||||||
Underline,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event<'a> {
|
pub enum Event<'a> {
|
||||||
Start(Container<'a>),
|
Start(&'a Element<'a>),
|
||||||
End(Container<'a>),
|
End(&'a Element<'a>),
|
||||||
Rule,
|
|
||||||
BabelCall(&'a BabelCall<'a>),
|
|
||||||
Clock(&'a Clock<'a>),
|
|
||||||
Cookie(&'a Cookie<'a>),
|
|
||||||
Drawer(&'a Drawer<'a>),
|
|
||||||
FnDef(&'a FnDef<'a>),
|
|
||||||
FnRef(&'a FnRef<'a>),
|
|
||||||
InlineCall(&'a InlineCall<'a>),
|
|
||||||
InlineSrc(&'a InlineSrc<'a>),
|
|
||||||
Keyword(&'a Keyword<'a>),
|
|
||||||
Link(&'a Link<'a>),
|
|
||||||
Macros(&'a Macros<'a>),
|
|
||||||
Planning(Planning<'a>),
|
|
||||||
RadioTarget(&'a RadioTarget<'a>),
|
|
||||||
Snippet(&'a Snippet<'a>),
|
|
||||||
Target(&'a Target<'a>),
|
|
||||||
Timestamp(&'a Timestamp<'a>),
|
|
||||||
Code(&'a str),
|
|
||||||
Comment(&'a str),
|
|
||||||
FixedWidth(&'a str),
|
|
||||||
Text(&'a str),
|
|
||||||
Verbatim(&'a str),
|
|
||||||
}
|
|
||||||
|
|
||||||
enum State {
|
|
||||||
Start,
|
|
||||||
End,
|
|
||||||
Empty,
|
|
||||||
Finished,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Iter<'a> {
|
pub struct Iter<'a> {
|
||||||
arena: &'a Arena<Element<'a>>,
|
pub(crate) arena: &'a Arena<Element<'a>>,
|
||||||
node: NodeId,
|
pub(crate) traverse: Traverse<'a, Element<'a>>,
|
||||||
state: State,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iter<'a> {
|
|
||||||
pub(crate) fn new(arena: &'a Arena<Element<'a>>, node: NodeId) -> Self {
|
|
||||||
Iter {
|
|
||||||
arena,
|
|
||||||
node,
|
|
||||||
state: State::Start,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_event(&mut self) -> Option<Event<'a>> {
|
|
||||||
let node = &self.arena[self.node];
|
|
||||||
match &node.data {
|
|
||||||
Element::Root => {
|
|
||||||
self.state = State::Finished;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Element::BabelCall { call, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::BabelCall(call))
|
|
||||||
}
|
|
||||||
Element::Verbatim { value, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Verbatim(value))
|
|
||||||
}
|
|
||||||
Element::Code { value, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Code(value))
|
|
||||||
}
|
|
||||||
Element::Text { value, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Text(value))
|
|
||||||
}
|
|
||||||
Element::Block { block, .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::Block(block)))
|
|
||||||
}
|
|
||||||
Element::Bold { .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::Bold))
|
|
||||||
}
|
|
||||||
Element::Document { .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Event::Start(Container::Document))
|
|
||||||
}
|
|
||||||
Element::DynBlock { dyn_block, .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::DynBlock(dyn_block)))
|
|
||||||
}
|
|
||||||
Element::Headline { headline, .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::Headline(headline)))
|
|
||||||
}
|
|
||||||
Element::Italic { .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::Italic))
|
|
||||||
}
|
|
||||||
Element::List { list, .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::List(list)))
|
|
||||||
}
|
|
||||||
Element::ListItem { list_item, .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::ListItem(list_item)))
|
|
||||||
}
|
|
||||||
Element::Paragraph { .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::Paragraph))
|
|
||||||
}
|
|
||||||
Element::Section { .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::Section))
|
|
||||||
}
|
|
||||||
Element::Strike { .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::Strike))
|
|
||||||
}
|
|
||||||
Element::Underline { .. } => {
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
self.state = State::Empty;
|
|
||||||
} else {
|
|
||||||
self.state = State::Start;
|
|
||||||
}
|
|
||||||
Some(Event::Start(Container::Underline))
|
|
||||||
}
|
|
||||||
Element::Clock { clock, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Clock(clock))
|
|
||||||
}
|
|
||||||
Element::Cookie { cookie, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Cookie(cookie))
|
|
||||||
}
|
|
||||||
Element::Drawer { drawer, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Drawer(drawer))
|
|
||||||
}
|
|
||||||
Element::FnDef { fn_def, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::FnDef(fn_def))
|
|
||||||
}
|
|
||||||
Element::FnRef { fn_ref, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::FnRef(fn_ref))
|
|
||||||
}
|
|
||||||
Element::InlineCall { inline_call, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::InlineCall(inline_call))
|
|
||||||
}
|
|
||||||
Element::InlineSrc { inline_src, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::InlineSrc(inline_src))
|
|
||||||
}
|
|
||||||
Element::Keyword { keyword, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Keyword(keyword))
|
|
||||||
}
|
|
||||||
Element::Link { link, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Link(link))
|
|
||||||
}
|
|
||||||
Element::Macros { macros, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Macros(macros))
|
|
||||||
}
|
|
||||||
Element::Planning {
|
|
||||||
deadline,
|
|
||||||
scheduled,
|
|
||||||
closed,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Planning(Planning {
|
|
||||||
deadline: deadline.and_then(|id| {
|
|
||||||
if let Element::Timestamp { timestamp, .. } = &self.arena[id].data {
|
|
||||||
Some(timestamp)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
scheduled: scheduled.and_then(|id| {
|
|
||||||
if let Element::Timestamp { timestamp, .. } = &self.arena[id].data {
|
|
||||||
Some(timestamp)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
closed: closed.and_then(|id| {
|
|
||||||
if let Element::Timestamp { timestamp, .. } = &self.arena[id].data {
|
|
||||||
Some(timestamp)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
Element::RadioTarget { radio_target, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::RadioTarget(radio_target))
|
|
||||||
}
|
|
||||||
Element::Rule { .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Rule)
|
|
||||||
}
|
|
||||||
Element::Snippet { snippet, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Snippet(snippet))
|
|
||||||
}
|
|
||||||
Element::Target { target, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Target(target))
|
|
||||||
}
|
|
||||||
Element::Timestamp { timestamp, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Timestamp(timestamp))
|
|
||||||
}
|
|
||||||
Element::FixedWidth { value, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::FixedWidth(value))
|
|
||||||
}
|
|
||||||
Element::Comment { value, .. } => {
|
|
||||||
self.state = State::Start;
|
|
||||||
Some(Event::Comment(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end_event(&mut self) -> Option<Event<'a>> {
|
|
||||||
let node = &self.arena[self.node];
|
|
||||||
match &node.data {
|
|
||||||
Element::Root => {
|
|
||||||
self.state = State::Finished;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Element::Block { block, .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Block(block)))
|
|
||||||
}
|
|
||||||
Element::Bold { .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Bold))
|
|
||||||
}
|
|
||||||
Element::Document { .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Document))
|
|
||||||
}
|
|
||||||
Element::DynBlock { dyn_block, .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::DynBlock(dyn_block)))
|
|
||||||
}
|
|
||||||
Element::Headline { headline, .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Headline(headline)))
|
|
||||||
}
|
|
||||||
Element::Italic { .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Italic))
|
|
||||||
}
|
|
||||||
Element::List { list, .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::List(list)))
|
|
||||||
}
|
|
||||||
Element::ListItem { list_item, .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::ListItem(list_item)))
|
|
||||||
}
|
|
||||||
Element::Paragraph { .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Paragraph))
|
|
||||||
}
|
|
||||||
Element::Section { .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Section))
|
|
||||||
}
|
|
||||||
Element::Strike { .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Strike))
|
|
||||||
}
|
|
||||||
Element::Underline { .. } => {
|
|
||||||
self.state = State::End;
|
|
||||||
Some(Event::End(Container::Underline))
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
impl<'a> Iterator for Iter<'a> {
|
||||||
type Item = Event<'a>;
|
type Item = Event<'a>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.state {
|
self.traverse.next().map(|edge| match edge {
|
||||||
State::Finished => None,
|
NodeEdge::Start(e) => Event::Start(&self.arena[e].data),
|
||||||
State::End => {
|
NodeEdge::End(e) => Event::End(&self.arena[e].data),
|
||||||
let node = &self.arena[self.node];
|
})
|
||||||
if let Some(sibling_node) = node.next_sibling() {
|
|
||||||
self.node = sibling_node;
|
|
||||||
self.start_event()
|
|
||||||
} else if let Some(parent_node) = node.parent() {
|
|
||||||
self.node = parent_node;
|
|
||||||
self.end_event()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
State::Start => {
|
|
||||||
let node = &self.arena[self.node];
|
|
||||||
if let Some(child_node) = node.first_child() {
|
|
||||||
self.node = child_node;
|
|
||||||
self.start_event()
|
|
||||||
} else if let Some(sibling_node) = node.next_sibling() {
|
|
||||||
self.node = sibling_node;
|
|
||||||
self.start_event()
|
|
||||||
} else if let Some(parent_node) = node.parent() {
|
|
||||||
self.node = parent_node;
|
|
||||||
self.end_event()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
State::Empty => self.end_event(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,5 +146,6 @@ pub mod org;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod serde;
|
mod serde;
|
||||||
|
|
||||||
pub use iter::{Container, Event};
|
pub use elements::Element;
|
||||||
|
pub use iter::{Event, Iter};
|
||||||
pub use org::Org;
|
pub use org::Org;
|
||||||
|
|
31
src/org.rs
31
src/org.rs
|
@ -10,25 +10,21 @@ use crate::iter::Iter;
|
||||||
pub struct Org<'a> {
|
pub struct Org<'a> {
|
||||||
pub(crate) arena: Arena<Element<'a>>,
|
pub(crate) arena: Arena<Element<'a>>,
|
||||||
pub(crate) document: NodeId,
|
pub(crate) document: NodeId,
|
||||||
root: NodeId,
|
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Org<'a> {
|
impl<'a> Org<'a> {
|
||||||
pub fn parse(text: &'a str) -> Self {
|
pub fn parse(text: &'a str) -> Self {
|
||||||
let mut arena = Arena::new();
|
let mut arena = Arena::new();
|
||||||
let root = arena.new_node(Element::Root);
|
|
||||||
let document = arena.new_node(Element::Document {
|
let document = arena.new_node(Element::Document {
|
||||||
begin: 0,
|
begin: 0,
|
||||||
end: text.len(),
|
end: text.len(),
|
||||||
contents_begin: 0,
|
contents_begin: 0,
|
||||||
contents_end: text.len(),
|
contents_end: text.len(),
|
||||||
});
|
});
|
||||||
root.append(document, &mut arena).unwrap();
|
|
||||||
|
|
||||||
let mut org = Org {
|
let mut org = Org {
|
||||||
arena,
|
arena,
|
||||||
root,
|
|
||||||
document,
|
document,
|
||||||
text,
|
text,
|
||||||
};
|
};
|
||||||
|
@ -38,7 +34,10 @@ impl<'a> Org<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&'a self) -> Iter<'a> {
|
pub fn iter(&'a self) -> Iter<'a> {
|
||||||
Iter::new(&self.arena, self.root)
|
Iter {
|
||||||
|
arena: &self.arena,
|
||||||
|
traverse: self.document.traverse(&self.arena),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn html<W, H, E>(&self, mut writer: W, mut handler: H) -> Result<(), E>
|
pub fn html<W, H, E>(&self, mut writer: W, mut handler: H) -> Result<(), E>
|
||||||
|
@ -53,28 +52,6 @@ impl<'a> Org<'a> {
|
||||||
match event {
|
match event {
|
||||||
Start(e) => handler.start(&mut writer, e)?,
|
Start(e) => handler.start(&mut writer, e)?,
|
||||||
End(e) => handler.end(&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)?,
|
|
||||||
Comment(e) => handler.comment(&mut writer, e)?,
|
|
||||||
FixedWidth(e) => handler.fixed_width(&mut writer, e)?,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue