From 0e58afada7c4794083e730d15798829c666d3a45 Mon Sep 17 00:00:00 2001 From: PoiScript Date: Mon, 12 Aug 2019 22:45:31 +0800 Subject: [PATCH] feat(node): finish headline inserting functions --- src/lib.rs | 7 +- src/node.rs | 280 ++++++++++++++++++++++++++++++++++++++++++++------ src/org.rs | 96 +++++++++-------- tests/node.rs | 44 ++++++++ 4 files changed, 352 insertions(+), 75 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4d0e8dd..37e67d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -223,7 +223,10 @@ mod node; mod org; mod parsers; +mod error; + pub use config::ParseConfig; pub use elements::Element; -pub use node::HeadlineNode; -pub use org::Org; +pub use error::OrgizeError; +pub use node::{DocumentNode, HeadlineNode}; +pub use org::{Event, Org}; diff --git a/src/node.rs b/src/node.rs index 55afd2e..c910c97 100644 --- a/src/node.rs +++ b/src/node.rs @@ -5,6 +5,7 @@ use crate::config::ParseConfig; use crate::elements::{Element, Title}; use crate::parsers::{parse_container, Container, OwnedArena}; use crate::Org; +use crate::OrgizeError; #[derive(Copy, Clone, Debug)] pub struct HeadlineNode { @@ -14,7 +15,7 @@ pub struct HeadlineNode { pub(crate) section_node: Option, } -impl<'a: 'b, 'b> HeadlineNode { +impl HeadlineNode { pub(crate) fn new(node: NodeId, level: usize, org: &Org<'_>) -> HeadlineNode { let title_node = org.arena[node].first_child().unwrap(); let section_node = if let Some(node) = org.arena[title_node].next_sibling() { @@ -38,7 +39,7 @@ impl<'a: 'b, 'b> HeadlineNode { self.level } - pub fn title(self, org: &'b Org<'a>) -> &'b Title<'a> { + pub fn title<'a: 'b, 'b>(self, org: &'b Org<'a>) -> &'b Title<'a> { if let Element::Title(title) = org.arena[self.title_node].get() { title } else { @@ -46,7 +47,7 @@ impl<'a: 'b, 'b> HeadlineNode { } } - pub fn title_mut(self, org: &'b mut Org<'a>) -> &'b mut Title<'a> { + pub fn title_mut<'a: 'b, 'b>(self, org: &'b mut Org<'a>) -> &'b mut Title<'a> { if let Element::Title(title) = org.arena[self.title_node].get_mut() { title } else { @@ -54,7 +55,7 @@ impl<'a: 'b, 'b> HeadlineNode { } } - pub fn set_title_content>>(self, content: S, org: &mut Org<'a>) { + pub fn set_title_content<'a, S: Into>>(self, content: S, org: &mut Org<'a>) { let content = content.into(); let children: Vec<_> = self.title_node.children(&org.arena).collect(); @@ -82,9 +83,13 @@ impl<'a: 'b, 'b> HeadlineNode { } self.title_mut(org).raw = content; + + if cfg!(debug_assertions) { + org.check().unwrap(); + } } - pub fn set_section_content>>(self, content: S, org: &mut Org<'a>) { + pub fn set_section_content<'a, S: Into>>(self, content: S, org: &mut Org<'a>) { let node = if let Some(node) = self.section_node { let children: Vec<_> = node.children(&org.arena).collect(); for child in children { @@ -109,11 +114,15 @@ impl<'a: 'b, 'b> HeadlineNode { &ParseConfig::default(), ), } + + if cfg!(debug_assertions) { + org.check().unwrap(); + } } pub fn parent(self, org: &Org<'_>) -> Option { org.arena[self.node].parent().map(|node| { - if let &Element::Headline { level } = org.arena[node].get() { + if let Element::Headline { level } = *org.arena[node].get() { HeadlineNode::new(node, level, org) } else { unreachable!() @@ -121,49 +130,262 @@ impl<'a: 'b, 'b> HeadlineNode { }) } + pub fn children<'c>(self, org: &'c Org<'_>) -> impl Iterator + 'c { + self.node.children(&org.arena).filter_map(move |node| { + if let Element::Headline { level } = *org.arena[node].get() { + Some(HeadlineNode::new(node, level, org)) + } else { + None + } + }) + } + + pub fn previous_headline(self, org: &Org<'_>) -> Option { + if let Some(node) = org.arena[self.node].previous_sibling() { + if let Element::Headline { level } = *org.arena[node].get() { + Some(HeadlineNode::new(node, level, org)) + } else { + debug_assert_eq!(node, self.section_node.unwrap()); + None + } + } else { + None + } + } + + pub fn next_headline(self, org: &Org<'_>) -> Option { + if let Some(node) = org.arena[self.node].next_sibling() { + if let Element::Headline { level } = *org.arena[node].get() { + Some(HeadlineNode::new(node, level, org)) + } else { + unreachable!() + } + } else { + None + } + } + pub fn detach(self, org: &mut Org<'_>) { self.node.detach(&mut org.arena); + + if cfg!(debug_assertions) { + org.check().unwrap(); + } } pub fn is_detached(self, org: &Org<'_>) -> bool { self.parent(&org).is_none() } - pub fn append(self, headline: &HeadlineNode, org: &mut Org<'_>) { - if self.is_detached(org) || headline.level <= self.level { - // TODO: return an error - return; - } else { - self.node.append(headline.node, &mut org.arena); + fn check_level(self, min: usize, max: Option) -> Result<(), OrgizeError> { + match max { + Some(max) if self.level > max || self.level < min => Err(OrgizeError::HeadlineLevel { + min: Some(min), + max: Some(max), + at: self.node, + }), + None if self.level < min => Err(OrgizeError::HeadlineLevel { + min: Some(min), + max: None, + at: self.node, + }), + _ => Ok(()), } } - pub fn prepend(self, headline: &HeadlineNode, org: &mut Org<'_>) { - if self.is_detached(org) || headline.level <= self.level { - // TODO: return an error - return; - } else if let Some(node) = self.section_node { + pub fn append(self, headline: HeadlineNode, org: &mut Org<'_>) -> Result<(), OrgizeError> { + if !headline.is_detached(org) { + return Err(OrgizeError::Detached { at: headline.node }); + } + + if let Some(last_headline) = org.headlines().last() { + headline.check_level(self.level + 1, Some(last_headline.level))?; + } else { + headline.check_level(self.level + 1, None)?; + } + + self.node.append(headline.node, &mut org.arena); + + if cfg!(debug_assertions) { + org.check().unwrap(); + } + + Ok(()) + } + + pub fn prepend(self, headline: HeadlineNode, org: &mut Org<'_>) -> Result<(), OrgizeError> { + if !headline.is_detached(org) { + return Err(OrgizeError::Detached { at: headline.node }); + } + + if let Some(first_headline) = self.children(org).next() { + headline.check_level(first_headline.level, None)?; + } else { + headline.check_level(self.level + 1, None)?; + } + + if let Some(node) = self.section_node { node.insert_after(headline.node, &mut org.arena); } else { self.title_node.insert_after(headline.node, &mut org.arena); } + + if cfg!(debug_assertions) { + org.check().unwrap(); + } + + Ok(()) } - pub fn insert_before(self, headline: &HeadlineNode, org: &mut Org<'_>) { - if self.is_detached(org) || headline.level < self.level { - // TODO: return an error - return; - } else { - self.node.insert_after(headline.node, &mut org.arena); + pub fn insert_before( + self, + headline: HeadlineNode, + org: &mut Org<'_>, + ) -> Result<(), OrgizeError> { + if !headline.is_detached(org) { + return Err(OrgizeError::Detached { at: headline.node }); } + + if let Some(previous) = self.previous_headline(org) { + headline.check_level(self.level, Some(previous.level))?; + } else { + headline.check_level(self.level, None)?; + } + + self.node.insert_before(headline.node, &mut org.arena); + + if cfg!(debug_assertions) { + org.check().unwrap(); + } + + Ok(()) } - pub fn insert_after(self, headline: &HeadlineNode, org: &mut Org<'_>) { - if self.is_detached(org) || headline.level < self.level { - // TODO: return an error - return; - } else { - self.node.insert_after(headline.node, &mut org.arena); + pub fn insert_after( + self, + headline: HeadlineNode, + org: &mut Org<'_>, + ) -> Result<(), OrgizeError> { + if !headline.is_detached(org) { + return Err(OrgizeError::Detached { at: headline.node }); } + + if let Some(next) = self.next_headline(org) { + headline.check_level(next.level, Some(self.level))?; + } else if let Some(parent) = self.parent(org) { + headline.check_level(parent.level + 1, Some(self.level))?; + } else { + headline.check_level(1, Some(self.level))?; + } + + self.node.insert_after(headline.node, &mut org.arena); + + if cfg!(debug_assertions) { + org.check().unwrap(); + } + + Ok(()) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct DocumentNode { + pub(crate) section_node: Option, +} + +impl DocumentNode { + pub(crate) fn new(org: &Org<'_>) -> DocumentNode { + if let Some(node) = org.arena[org.root].first_child() { + if let Element::Section = org.arena[node].get() { + DocumentNode { + section_node: Some(node), + } + } else { + DocumentNode { section_node: None } + } + } else { + DocumentNode { section_node: None } + } + } + + pub fn children<'c>(self, org: &'c Org<'_>) -> impl Iterator + 'c { + org.root.children(&org.arena).filter_map(move |node| { + if let Element::Headline { level } = *org.arena[node].get() { + Some(HeadlineNode::new(node, level, org)) + } else { + None + } + }) + } + + pub fn set_section_content<'a, S: Into>>(self, content: S, org: &mut Org<'a>) { + let node = if let Some(node) = self.section_node { + let children: Vec<_> = node.children(&org.arena).collect(); + for child in children { + child.detach(&mut org.arena); + } + node + } else { + let node = org.arena.new_node(Element::Section); + org.root.append(node, &mut org.arena); + node + }; + + match content.into() { + Cow::Borrowed(content) => parse_container( + &mut org.arena, + Container::Block { node, content }, + &ParseConfig::default(), + ), + Cow::Owned(ref content) => parse_container( + &mut OwnedArena::new(&mut org.arena), + Container::Block { node, content }, + &ParseConfig::default(), + ), + } + + if cfg!(debug_assertions) { + org.check().unwrap(); + } + } + + pub fn append(self, headline: HeadlineNode, org: &mut Org<'_>) -> Result<(), OrgizeError> { + if !headline.is_detached(org) { + return Err(OrgizeError::Detached { at: headline.node }); + } + + if let Some(last_headline) = org.headlines().last() { + headline.check_level(1, Some(last_headline.level))?; + } + + org.root.append(headline.node, &mut org.arena); + + if cfg!(debug_assertions) { + org.check().unwrap(); + } + + Ok(()) + } + + pub fn prepend(self, headline: HeadlineNode, org: &mut Org<'_>) -> Result<(), OrgizeError> { + if !headline.is_detached(org) { + return Err(OrgizeError::Detached { at: headline.node }); + } + + if let Some(first_headline) = self.children(org).next() { + headline.check_level(first_headline.level, None)?; + } + + if let Some(node) = self.section_node { + node.insert_after(headline.node, &mut org.arena); + } else { + org.root.prepend(headline.node, &mut org.arena); + } + + if cfg!(debug_assertions) { + org.check().unwrap(); + } + + Ok(()) } } diff --git a/src/org.rs b/src/org.rs index 6d7841d..c04f499 100644 --- a/src/org.rs +++ b/src/org.rs @@ -4,12 +4,12 @@ use std::io::{Error, Write}; use crate::config::ParseConfig; use crate::elements::{Element, Title}; use crate::export::*; -use crate::node::HeadlineNode; +use crate::node::{DocumentNode, HeadlineNode}; use crate::parsers::{parse_container, Container}; pub struct Org<'a> { pub(crate) arena: Arena>, - root: NodeId, + pub(crate) root: NodeId, } #[derive(Debug)] @@ -18,7 +18,7 @@ pub enum Event<'a> { End(&'a Element<'a>), } -impl Org<'_> { +impl<'a> Org<'a> { pub fn new() -> Org<'static> { let mut arena = Arena::new(); let root = arena.new_node(Element::Document); @@ -26,15 +26,31 @@ impl Org<'_> { Org { arena, root } } - pub fn parse(text: &str) -> Org<'_> { + pub fn parse(text: &'a str) -> Org<'a> { Org::parse_with_config(text, &ParseConfig::default()) } - pub fn iter(&self) -> impl Iterator> + '_ { - self.root.traverse(&self.arena).map(move |edge| match edge { - NodeEdge::Start(e) => Event::Start(self.arena[e].get()), - NodeEdge::End(e) => Event::End(self.arena[e].get()), - }) + pub fn parse_with_config(content: &'a str, config: &ParseConfig) -> Org<'a> { + let mut org = Org::new(); + + parse_container( + &mut org.arena, + Container::Document { + content, + node: org.root, + }, + config, + ); + + if cfg!(debug_assertions) { + org.check().unwrap(); + } + + org + } + + pub fn document(&self) -> DocumentNode { + DocumentNode::new(self) } pub fn headlines(&self) -> impl Iterator + '_ { @@ -47,6 +63,33 @@ impl Org<'_> { }) } + pub fn arena(&self) -> &Arena> { + &self.arena + } + + pub fn new_headline(&mut self, title: Title<'a>) -> HeadlineNode { + let level = title.level; + let title_raw = title.raw.clone(); + let headline_node = self.arena.new_node(Element::Headline { level }); + let title_node = self.arena.new_node(Element::Title(title)); + headline_node.append(title_node, &mut self.arena); + let headline_node = HeadlineNode { + node: headline_node, + level, + title_node, + section_node: None, + }; + headline_node.set_title_content(title_raw, self); + headline_node + } + + pub fn iter(&self) -> impl Iterator> + '_ { + self.root.traverse(&self.arena).map(move |edge| match edge { + NodeEdge::Start(e) => Event::Start(self.arena[e].get()), + NodeEdge::End(e) => Event::End(self.arena[e].get()), + }) + } + pub fn html(&self, wrtier: W) -> Result<(), Error> { self.html_with_handler(wrtier, DefaultHtmlHandler) } @@ -88,41 +131,6 @@ impl Org<'_> { } } -impl<'a> Org<'a> { - pub fn parse_with_config(content: &'a str, config: &ParseConfig) -> Org<'a> { - let mut org = Org::new(); - - parse_container( - &mut org.arena, - Container::Document { - content, - node: org.root, - }, - config, - ); - - org - } - - pub fn new_headline(&mut self, title: Title<'a>) -> HeadlineNode { - let title_level = title.level; - let title_raw = title.raw.clone(); - let headline_node = self - .arena - .new_node(Element::Headline { level: title_level }); - let title_node = self.arena.new_node(Element::Title(title)); - headline_node.append(title_node, &mut self.arena); - let headline_node = HeadlineNode { - node: headline_node, - level: title_level, - title_node, - section_node: None, - }; - headline_node.set_title_content(title_raw, self); - headline_node - } -} - #[cfg(feature = "ser")] use serde::{ser::Serializer, Serialize}; diff --git a/tests/node.rs b/tests/node.rs index 20c7d2f..f4aa094 100644 --- a/tests/node.rs +++ b/tests/node.rs @@ -1,5 +1,7 @@ +use orgize::elements::Title; use orgize::Org; use pretty_assertions::assert_eq; +use serde_json::to_string; #[test] fn set_content() { @@ -22,3 +24,45 @@ section 1

a bold title

and a underline section

" ); } + +#[test] +fn insert() { + let mut org = Org::new(); + let document = org.document(); + + let h1 = org.new_headline(Title { + level: 1, + raw: "title".into(), + ..Default::default() + }); + h1.set_section_content("section", &mut org); + document.prepend(h1, &mut org).unwrap(); + dbg!(to_string(&org).unwrap()); + + let h3 = org.new_headline(Title { + level: 3, + raw: "title".into(), + ..Default::default() + }); + h3.set_section_content("section", &mut org); + document.prepend(h3, &mut org).unwrap(); + dbg!(to_string(&org).unwrap()); + + let h2 = org.new_headline(Title { + level: 2, + raw: "title".into(), + ..Default::default() + }); + h2.set_section_content("section", &mut org); + h1.insert_before(h2, &mut org).unwrap(); + dbg!(to_string(&org).unwrap()); + + let mut writer = Vec::new(); + org.html(&mut writer).unwrap(); + assert_eq!( + String::from_utf8(writer).unwrap(), + "

title

section

\ +

title

section

\ +

title

section

" + ); +}