feat(config): add done_keywords
field
This commit is contained in:
parent
3e82172dfe
commit
b429e4f54a
30
README.md
30
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<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
|
||||
|
||||
|
|
|
@ -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)?);
|
||||
}
|
||||
|
|
|
@ -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")],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
),
|
||||
|
|
32
src/lib.rs
32
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<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;
|
||||
|
||||
|
|
|
@ -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()))
|
||||
|
|
Loading…
Reference in a new issue