feat(config): add done_keywords field

This commit is contained in:
PoiScript 2019-07-30 21:09:29 +08:00
parent 3e82172dfe
commit b429e4f54a
6 changed files with 51 additions and 56 deletions

View file

@ -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<FromUtf8Error> for MyError {
}
}
struct MyHtmlHandler;
struct MyHtmlHandler(DefaultHtmlHandler);
impl HtmlHandler<MyError> for MyHtmlHandler {
fn start<W: Write>(&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,
"<h{0}><a id=\"{1}\" href=\"#{1}\">{2}</a></h{0}>",
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<MyError> 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

View file

@ -29,28 +29,26 @@ impl From<FromUtf8Error> for MyError {
}
}
struct MyHtmlHandler;
struct MyHtmlHandler(DefaultHtmlHandler);
impl HtmlHandler<MyError> for MyHtmlHandler {
fn start<W: Write>(&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,
"<h{0}><a id=\"{1}\" href=\"#{1}\">{2}</a></h{0}>",
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)?);
}

View file

@ -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<String>,
/// Headline's TODO keywords, done type
pub done_keywords: Vec<String>,
}
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")],
}
}
}

View file

@ -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()
}
),

View file

@ -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<MyError> for MyHtmlHandler {
//! fn start<W: Write>(&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,
//! "<h{0}><a id=\"{1}\" href=\"#{1}\">{2}</a></h{0}>",
//! 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;

View file

@ -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()))