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
|
``` rust
|
||||||
use orgize::{Org, ParseConfig};
|
use orgize::{Org, ParseConfig};
|
||||||
|
|
||||||
let org = Org::parse_with_config(
|
Org::parse_with_config(
|
||||||
"* TASK Title 1",
|
"* TASK Title 1",
|
||||||
ParseConfig {
|
&ParseConfig {
|
||||||
// custom todo keywords
|
// custom todo keywords
|
||||||
todo_keywords: &["TASK"],
|
todo_keywords: vec!["TASK".to_string()],
|
||||||
..Default::default()
|
..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
|
To customize html rendering, simply implementing `HtmlHandler` trait and passing
|
||||||
it to the `Org::html_with_handler` function.
|
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 {
|
impl HtmlHandler<MyError> for MyHtmlHandler {
|
||||||
fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> {
|
fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> {
|
||||||
let mut default_handler = DefaultHtmlHandler;
|
|
||||||
match element {
|
match element {
|
||||||
Element::Headline(headline) => {
|
Element::Headline(headline) => {
|
||||||
if headline.level > 6 {
|
if headline.level > 6 {
|
||||||
return Err(MyError::Heading);
|
return Err(MyError::Heading);
|
||||||
} else {
|
} else {
|
||||||
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}>",
|
||||||
headline.level,
|
headline.level,
|
||||||
slugify,
|
slugify!(headline.title),
|
||||||
Escape(headline.title),
|
Escape(headline.title),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fallthrough to default handler
|
// fallthrough to default handler
|
||||||
_ => default_handler.start(w, element)?,
|
_ => self.0.start(w, element)?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -130,7 +128,7 @@ impl HtmlHandler<MyError> for MyHtmlHandler {
|
||||||
|
|
||||||
fn main() -> Result<(), MyError> {
|
fn main() -> Result<(), MyError> {
|
||||||
let mut writer = Vec::new();
|
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!(
|
assert_eq!(
|
||||||
String::from_utf8(writer)?,
|
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.
|
**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.
|
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
|
So if you want to change how a non-container element renders, just redefine the `start`
|
||||||
function and leave the end function untouched.
|
function and leave the `end` function unchanged.
|
||||||
|
|
||||||
## Serde
|
# Serde
|
||||||
|
|
||||||
`Org` struct have already implemented serde's `Serialize` trait. It means you can
|
`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
|
```rust
|
||||||
use orgize::Org;
|
use orgize::Org;
|
||||||
|
@ -186,11 +184,11 @@ println!("{}", to_string(&org).unwrap());
|
||||||
|
|
||||||
## Features
|
## 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.
|
+ `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
|
## License
|
||||||
|
|
||||||
|
|
|
@ -29,28 +29,26 @@ impl From<FromUtf8Error> for MyError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyHtmlHandler;
|
struct MyHtmlHandler(DefaultHtmlHandler);
|
||||||
|
|
||||||
impl HtmlHandler<MyError> for MyHtmlHandler {
|
impl HtmlHandler<MyError> for MyHtmlHandler {
|
||||||
fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> {
|
fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> {
|
||||||
let mut default_handler = DefaultHtmlHandler;
|
|
||||||
match element {
|
match element {
|
||||||
Element::Headline(headline) => {
|
Element::Headline(headline) => {
|
||||||
if headline.level > 6 {
|
if headline.level > 6 {
|
||||||
return Err(MyError::Heading);
|
return Err(MyError::Heading);
|
||||||
} else {
|
} else {
|
||||||
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}>",
|
||||||
headline.level,
|
headline.level,
|
||||||
slugify,
|
slugify!(headline.title),
|
||||||
Escape(headline.title),
|
Escape(headline.title),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fallthrough to default handler
|
// fallthrough to default handler
|
||||||
_ => default_handler.start(w, element)?,
|
_ => self.0.start(w, element)?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -65,7 +63,7 @@ fn main() -> Result<(), MyError> {
|
||||||
let contents = String::from_utf8(fs::read(&args[1])?)?;
|
let contents = String::from_utf8(fs::read(&args[1])?)?;
|
||||||
|
|
||||||
let mut writer = Vec::new();
|
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)?);
|
println!("{}", String::from_utf8(writer)?);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
/// Parse configuration
|
/// Parse configuration
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ParseConfig<'a> {
|
pub struct ParseConfig {
|
||||||
/// Default headline todo keywords, it shouldn't be changed.
|
/// Headline's TODO keywords, todo type
|
||||||
pub default_todo_keywords: &'a [&'a str],
|
pub todo_keywords: Vec<String>,
|
||||||
/// Custom headline todo keywords
|
/// Headline's TODO keywords, done type
|
||||||
pub todo_keywords: &'a [&'a str],
|
pub done_keywords: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ParseConfig<'_> {
|
impl Default for ParseConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ParseConfig {
|
ParseConfig {
|
||||||
default_todo_keywords: &["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"],
|
todo_keywords: vec![String::from("TODO")],
|
||||||
todo_keywords: &[],
|
done_keywords: vec![String::from("DONE")],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub struct Headline<'a> {
|
||||||
impl Headline<'_> {
|
impl Headline<'_> {
|
||||||
pub(crate) fn parse<'a>(
|
pub(crate) fn parse<'a>(
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
config: &ParseConfig<'_>,
|
config: &ParseConfig,
|
||||||
) -> (&'a str, Headline<'a>, &'a str) {
|
) -> (&'a str, Headline<'a>, &'a str) {
|
||||||
let level = memchr2(b'\n', b' ', text.as_bytes()).unwrap_or_else(|| text.len());
|
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())
|
let (word, off) = memchr(b' ', tail.as_bytes())
|
||||||
.map(|i| (&tail[0..i], i + 1))
|
.map(|i| (&tail[0..i], i + 1))
|
||||||
.unwrap_or_else(|| (tail, tail.len()));
|
.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..])
|
(Some(word), &tail[off..])
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,7 +152,7 @@ impl Headline<'_> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref CONFIG: ParseConfig<'static> = ParseConfig::default();
|
static ref CONFIG: ParseConfig = ParseConfig::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -276,7 +277,7 @@ fn parse_todo_keywords() {
|
||||||
Headline::parse(
|
Headline::parse(
|
||||||
"**** DONE [#A] COMMENT Title :tag:a2%:",
|
"**** DONE [#A] COMMENT Title :tag:a2%:",
|
||||||
&ParseConfig {
|
&ParseConfig {
|
||||||
default_todo_keywords: &[],
|
done_keywords: vec![],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -296,7 +297,7 @@ fn parse_todo_keywords() {
|
||||||
Headline::parse(
|
Headline::parse(
|
||||||
"**** TASK [#A] COMMENT Title :tag:a2%:",
|
"**** TASK [#A] COMMENT Title :tag:a2%:",
|
||||||
&ParseConfig {
|
&ParseConfig {
|
||||||
todo_keywords: &["TASK"],
|
todo_keywords: vec!["TASK".to_string()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
32
src/lib.rs
32
src/lib.rs
|
@ -21,9 +21,9 @@
|
||||||
//!
|
//!
|
||||||
//! Org::parse_with_config(
|
//! Org::parse_with_config(
|
||||||
//! "* TASK Title 1",
|
//! "* TASK Title 1",
|
||||||
//! ParseConfig {
|
//! &ParseConfig {
|
||||||
//! // custom todo keywords
|
//! // custom todo keywords
|
||||||
//! todo_keywords: &["TASK"],
|
//! todo_keywords: vec!["TASK".to_string()],
|
||||||
//! ..Default::default()
|
//! ..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
|
//! To customize html rendering, simply implementing [`HtmlHandler`] trait and passing
|
||||||
//! it to the [`Org::html_with_handler`] function.
|
//! it to the [`Org::html_with_handler`] function.
|
||||||
|
@ -112,28 +112,26 @@
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! struct MyHtmlHandler;
|
//! struct MyHtmlHandler(DefaultHtmlHandler);
|
||||||
//!
|
//!
|
||||||
//! impl HtmlHandler<MyError> for MyHtmlHandler {
|
//! impl HtmlHandler<MyError> for MyHtmlHandler {
|
||||||
//! fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> {
|
//! fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<(), MyError> {
|
||||||
//! let mut default_handler = DefaultHtmlHandler;
|
|
||||||
//! match element {
|
//! match element {
|
||||||
//! Element::Headline(headline) => {
|
//! Element::Headline(headline) => {
|
||||||
//! if headline.level > 6 {
|
//! if headline.level > 6 {
|
||||||
//! return Err(MyError::Heading);
|
//! return Err(MyError::Heading);
|
||||||
//! } else {
|
//! } else {
|
||||||
//! 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}>",
|
||||||
//! headline.level,
|
//! headline.level,
|
||||||
//! slugify,
|
//! slugify!(headline.title),
|
||||||
//! Escape(headline.title),
|
//! Escape(headline.title),
|
||||||
//! )?;
|
//! )?;
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! // fallthrough to default handler
|
//! // fallthrough to default handler
|
||||||
//! _ => default_handler.start(w, element)?,
|
//! _ => self.0.start(w, element)?,
|
||||||
//! }
|
//! }
|
||||||
//! Ok(())
|
//! Ok(())
|
||||||
//! }
|
//! }
|
||||||
|
@ -141,7 +139,7 @@
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), MyError> {
|
//! fn main() -> Result<(), MyError> {
|
||||||
//! let mut writer = Vec::new();
|
//! 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!(
|
//! assert_eq!(
|
||||||
//! String::from_utf8(writer)?,
|
//! String::from_utf8(writer)?,
|
||||||
|
@ -156,13 +154,13 @@
|
||||||
//! **Note**: as I mentioned above, each element will appears two times while iterating.
|
//! **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.
|
//! 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
|
//! So if you want to change how a non-container element renders, just redefine the `start`
|
||||||
//! function and leave the end function untouched.
|
//! function and leave the `end` function unchanged.
|
||||||
//!
|
//!
|
||||||
//! # Serde
|
//! # Serde
|
||||||
//!
|
//!
|
||||||
//! `Org` struct have already implemented serde's `Serialize` trait. It means you can
|
//! `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
|
//! ```rust
|
||||||
//! use orgize::Org;
|
//! use orgize::Org;
|
||||||
|
@ -197,11 +195,11 @@
|
||||||
//!
|
//!
|
||||||
//! # Features
|
//! # 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.
|
//! + `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
|
//! # License
|
||||||
//!
|
//!
|
||||||
|
@ -209,11 +207,11 @@
|
||||||
|
|
||||||
#![allow(clippy::range_plus_one)]
|
#![allow(clippy::range_plus_one)]
|
||||||
|
|
||||||
pub mod config;
|
mod config;
|
||||||
pub mod elements;
|
pub mod elements;
|
||||||
pub mod export;
|
pub mod export;
|
||||||
pub mod iter;
|
mod iter;
|
||||||
pub mod org;
|
mod org;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod serde;
|
mod serde;
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,10 @@ enum Container<'a> {
|
||||||
|
|
||||||
impl<'a> Org<'a> {
|
impl<'a> Org<'a> {
|
||||||
pub fn parse(text: &'a str) -> Self {
|
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 mut arena = Arena::new();
|
||||||
let document = arena.new_node(Element::Document);
|
let document = arena.new_node(Element::Document);
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ fn parse_element<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if tail.starts_with(':') {
|
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)));
|
return Some((tail, arena.new_node(drawer)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -404,7 +404,7 @@ fn parse_object<'a>(
|
||||||
.ok()
|
.ok()
|
||||||
.map(|(tail, element)| (tail, arena.new_node(element))),
|
.map(|(tail, element)| (tail, arena.new_node(element))),
|
||||||
b'<' => RadioTarget::parse(contents)
|
b'<' => RadioTarget::parse(contents)
|
||||||
.map(|(tail, (radio, content))| (tail, radio))
|
.map(|(tail, (radio, _content))| (tail, radio))
|
||||||
.or_else(|_| Target::parse(contents))
|
.or_else(|_| Target::parse(contents))
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
Timestamp::parse_active(contents).map(|(tail, timestamp)| (tail, timestamp.into()))
|
Timestamp::parse_active(contents).map(|(tail, timestamp)| (tail, timestamp.into()))
|
||||||
|
|
Loading…
Reference in a new issue