From b429e4f54addaa74bd26873228d34890334897e8 Mon Sep 17 00:00:00 2001 From: PoiScript Date: Tue, 30 Jul 2019 21:09:29 +0800 Subject: [PATCH] feat(config): add `done_keywords` field --- README.md | 30 ++++++++++++++---------------- examples/custom.rs | 10 ++++------ src/config.rs | 16 ++++++++-------- src/elements/headline.rs | 11 ++++++----- src/lib.rs | 32 +++++++++++++++----------------- src/org.rs | 8 ++++---- 6 files changed, 51 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index d98571b..d1d6507 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ or `Org::parse_with_config`: ``` rust use orgize::{Org, ParseConfig}; -let org = Org::parse_with_config( +Org::parse_with_config( "* TASK Title 1", - ParseConfig { + &ParseConfig { // custom todo keywords - todo_keywords: &["TASK"], + todo_keywords: vec!["TASK".to_string()], ..Default::default() }, ); @@ -64,7 +64,7 @@ assert_eq!( ); ``` -## Render html with custom HtmlHandler +## Render html with custom `HtmlHandler` To customize html rendering, simply implementing `HtmlHandler` trait and passing it to the `Org::html_with_handler` function. @@ -101,28 +101,26 @@ impl From for MyError { } } -struct MyHtmlHandler; +struct MyHtmlHandler(DefaultHtmlHandler); impl HtmlHandler for MyHtmlHandler { fn start(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> { - let mut default_handler = DefaultHtmlHandler; match element { Element::Headline(headline) => { if headline.level > 6 { return Err(MyError::Heading); } else { - let slugify = slugify!(headline.title); write!( w, "{2}", headline.level, - slugify, + slugify!(headline.title), Escape(headline.title), )?; } } // fallthrough to default handler - _ => default_handler.start(w, element)?, + _ => self.0.start(w, element)?, } Ok(()) } @@ -130,7 +128,7 @@ impl HtmlHandler for MyHtmlHandler { fn main() -> Result<(), MyError> { let mut writer = Vec::new(); - Org::parse("* title\n*section*").html_with_handler(&mut writer, MyHtmlHandler)?; + Org::parse("* title\n*section*").html_with_handler(&mut writer, MyHtmlHandler(DefaultHtmlHandler))?; assert_eq!( String::from_utf8(writer)?, @@ -145,13 +143,13 @@ fn main() -> Result<(), MyError> { **Note**: as I mentioned above, each element will appears two times while iterating. And handler will silently ignores all end events from non-container elements. -So if you want to change how a non-container element renders, just redefine the start -function and leave the end function untouched. +So if you want to change how a non-container element renders, just redefine the `start` +function and leave the `end` function unchanged. -## Serde +# Serde `Org` struct have already implemented serde's `Serialize` trait. It means you can -freely serialize it into any format that serde supports such as json: +serialize it into any format supported by serde, such as json: ```rust use orgize::Org; @@ -186,11 +184,11 @@ println!("{}", to_string(&org).unwrap()); ## Features -By now, orgize provides three features: +By now, orgize provides two features: + `serde`: adds the ability to serialize `Org` and other elements using `serde`, enabled by default. -+ `chrono`: adds the ability to convert `Datetime` into `chrono` struct, disabled by default. ++ `chrono`: adds the ability to convert `Datetime` into `chrono` structs, disabled by default. ## License diff --git a/examples/custom.rs b/examples/custom.rs index fd304aa..7d96f65 100644 --- a/examples/custom.rs +++ b/examples/custom.rs @@ -29,28 +29,26 @@ impl From for MyError { } } -struct MyHtmlHandler; +struct MyHtmlHandler(DefaultHtmlHandler); impl HtmlHandler for MyHtmlHandler { fn start(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> { - let mut default_handler = DefaultHtmlHandler; match element { Element::Headline(headline) => { if headline.level > 6 { return Err(MyError::Heading); } else { - let slugify = slugify!(headline.title); write!( w, "{2}", headline.level, - slugify, + slugify!(headline.title), Escape(headline.title), )?; } } // fallthrough to default handler - _ => default_handler.start(w, element)?, + _ => self.0.start(w, element)?, } Ok(()) } @@ -65,7 +63,7 @@ fn main() -> Result<(), MyError> { let contents = String::from_utf8(fs::read(&args[1])?)?; let mut writer = Vec::new(); - Org::parse(&contents).html_with_handler(&mut writer, MyHtmlHandler)?; + Org::parse(&contents).html_with_handler(&mut writer, MyHtmlHandler(DefaultHtmlHandler))?; println!("{}", String::from_utf8(writer)?); } diff --git a/src/config.rs b/src/config.rs index 5b829d8..6175baa 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,18 +2,18 @@ /// Parse configuration #[derive(Clone, Debug)] -pub struct ParseConfig<'a> { - /// Default headline todo keywords, it shouldn't be changed. - pub default_todo_keywords: &'a [&'a str], - /// Custom headline todo keywords - pub todo_keywords: &'a [&'a str], +pub struct ParseConfig { + /// Headline's TODO keywords, todo type + pub todo_keywords: Vec, + /// Headline's TODO keywords, done type + pub done_keywords: Vec, } -impl Default for ParseConfig<'_> { +impl Default for ParseConfig { fn default() -> Self { ParseConfig { - default_todo_keywords: &["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"], - todo_keywords: &[], + todo_keywords: vec![String::from("TODO")], + done_keywords: vec![String::from("DONE")], } } } diff --git a/src/elements/headline.rs b/src/elements/headline.rs index 92d38a5..0e57d83 100644 --- a/src/elements/headline.rs +++ b/src/elements/headline.rs @@ -27,7 +27,7 @@ pub struct Headline<'a> { impl Headline<'_> { pub(crate) fn parse<'a>( text: &'a str, - config: &ParseConfig<'_>, + config: &ParseConfig, ) -> (&'a str, Headline<'a>, &'a str) { let level = memchr2(b'\n', b' ', text.as_bytes()).unwrap_or_else(|| text.len()); @@ -67,7 +67,8 @@ impl Headline<'_> { let (word, off) = memchr(b' ', tail.as_bytes()) .map(|i| (&tail[0..i], i + 1)) .unwrap_or_else(|| (tail, tail.len())); - if config.todo_keywords.contains(&word) || config.default_todo_keywords.contains(&word) + if config.todo_keywords.iter().any(|x| x == word) + || config.done_keywords.iter().any(|x| x == word) { (Some(word), &tail[off..]) } else { @@ -151,7 +152,7 @@ impl Headline<'_> { #[cfg(test)] lazy_static::lazy_static! { - static ref CONFIG: ParseConfig<'static> = ParseConfig::default(); + static ref CONFIG: ParseConfig = ParseConfig::default(); } #[test] @@ -276,7 +277,7 @@ fn parse_todo_keywords() { Headline::parse( "**** DONE [#A] COMMENT Title :tag:a2%:", &ParseConfig { - default_todo_keywords: &[], + done_keywords: vec![], ..Default::default() } ), @@ -296,7 +297,7 @@ fn parse_todo_keywords() { Headline::parse( "**** TASK [#A] COMMENT Title :tag:a2%:", &ParseConfig { - todo_keywords: &["TASK"], + todo_keywords: vec!["TASK".to_string()], ..Default::default() } ), diff --git a/src/lib.rs b/src/lib.rs index 5618724..5163d95 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,9 +21,9 @@ //! //! Org::parse_with_config( //! "* TASK Title 1", -//! ParseConfig { +//! &ParseConfig { //! // custom todo keywords -//! todo_keywords: &["TASK"], +//! todo_keywords: vec!["TASK".to_string()], //! ..Default::default() //! }, //! ); @@ -72,7 +72,7 @@ //! ); //! ``` //! -//! # Render html with custom HtmlHandler +//! # Render html with custom `HtmlHandler` //! //! To customize html rendering, simply implementing [`HtmlHandler`] trait and passing //! it to the [`Org::html_with_handler`] function. @@ -112,28 +112,26 @@ //! } //! } //! -//! struct MyHtmlHandler; +//! struct MyHtmlHandler(DefaultHtmlHandler); //! //! impl HtmlHandler for MyHtmlHandler { //! fn start(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> { -//! let mut default_handler = DefaultHtmlHandler; //! match element { //! Element::Headline(headline) => { //! if headline.level > 6 { //! return Err(MyError::Heading); //! } else { -//! let slugify = slugify!(headline.title); //! write!( //! w, //! "{2}", //! headline.level, -//! slugify, +//! slugify!(headline.title), //! Escape(headline.title), //! )?; //! } //! } //! // fallthrough to default handler -//! _ => default_handler.start(w, element)?, +//! _ => self.0.start(w, element)?, //! } //! Ok(()) //! } @@ -141,7 +139,7 @@ //! //! fn main() -> Result<(), MyError> { //! let mut writer = Vec::new(); -//! Org::parse("* title\n*section*").html_with_handler(&mut writer, MyHtmlHandler)?; +//! Org::parse("* title\n*section*").html_with_handler(&mut writer, MyHtmlHandler(DefaultHtmlHandler))?; //! //! assert_eq!( //! String::from_utf8(writer)?, @@ -156,13 +154,13 @@ //! **Note**: as I mentioned above, each element will appears two times while iterating. //! And handler will silently ignores all end events from non-container elements. //! -//! So if you want to change how a non-container element renders, just redefine the start -//! function and leave the end function untouched. +//! So if you want to change how a non-container element renders, just redefine the `start` +//! function and leave the `end` function unchanged. //! //! # Serde //! //! `Org` struct have already implemented serde's `Serialize` trait. It means you can -//! freely serialize it into any format that serde supports such as json: +//! serialize it into any format supported by serde, such as json: //! //! ```rust //! use orgize::Org; @@ -197,11 +195,11 @@ //! //! # Features //! -//! By now, orgize provides three features: +//! By now, orgize provides two features: //! //! + `serde`: adds the ability to serialize `Org` and other elements using `serde`, enabled by default. //! -//! + `chrono`: adds the ability to convert `Datetime` into `chrono` struct, disabled by default. +//! + `chrono`: adds the ability to convert `Datetime` into `chrono` structs, disabled by default. //! //! # License //! @@ -209,11 +207,11 @@ #![allow(clippy::range_plus_one)] -pub mod config; +mod config; pub mod elements; pub mod export; -pub mod iter; -pub mod org; +mod iter; +mod org; #[cfg(feature = "serde")] mod serde; diff --git a/src/org.rs b/src/org.rs index 56a964a..143d1e3 100644 --- a/src/org.rs +++ b/src/org.rs @@ -44,10 +44,10 @@ enum Container<'a> { impl<'a> Org<'a> { pub fn parse(text: &'a str) -> Self { - Org::parse_with_config(text, ParseConfig::default()) + Org::parse_with_config(text, &ParseConfig::default()) } - pub fn parse_with_config(content: &'a str, config: ParseConfig<'_>) -> Self { + pub fn parse_with_config(content: &'a str, config: &ParseConfig) -> Self { let mut arena = Arena::new(); let document = arena.new_node(Element::Document); @@ -238,7 +238,7 @@ fn parse_element<'a>( } if tail.starts_with(':') { - if let Some((tail, drawer, content)) = Drawer::parse(tail) { + if let Some((tail, drawer, _content)) = Drawer::parse(tail) { return Some((tail, arena.new_node(drawer))); } } @@ -404,7 +404,7 @@ fn parse_object<'a>( .ok() .map(|(tail, element)| (tail, arena.new_node(element))), b'<' => RadioTarget::parse(contents) - .map(|(tail, (radio, content))| (tail, radio)) + .map(|(tail, (radio, _content))| (tail, radio)) .or_else(|_| Target::parse(contents)) .or_else(|_| { Timestamp::parse_active(contents).map(|(tail, timestamp)| (tail, timestamp.into()))