diff --git a/README.md b/README.md
index d173c09..d8c3837 100644
--- a/README.md
+++ b/README.md
@@ -13,14 +13,22 @@ To parse a orgmode string, simply invoking the `Org::parse` function:
```rust
use orgize::Org;
-let org = Org::parse(r#"* Title 1
-*Section 1*
-** Title 2
-_Section 2_
-* Title 3
-/Section 3/
-* Title 4
-=Section 4="#);
+Org::parse("* DONE Title :tag:");
+```
+
+or `Org::parse_with_config`:
+
+``` rust
+use orgize::{Org, ParseConfig};
+
+let org = Org::parse_with_config(
+ "* TASK Title 1",
+ ParseConfig {
+ // custom todo keywords
+ todo_keywords: &["TASK"],
+ ..Default::default()
+ },
+);
```
## Iter
@@ -29,7 +37,9 @@ _Section 2_
a simple wrapper of `Element`.
```rust
-for event in org.iter() {
+use orgize::Org;
+
+for event in Org::parse("* DONE Title :tag:").iter() {
// handling the event
}
```
@@ -43,15 +53,14 @@ You can call the `Org::html_default` function to generate html directly, which
uses the `DefaultHtmlHandler` internally:
```rust
+use orgize::Org;
+
let mut writer = Vec::new();
-org.html_default(&mut writer).unwrap();
+Org::parse("* title\n*section*").html_default(&mut writer).unwrap();
assert_eq!(
String::from_utf8(writer).unwrap(),
- "Title 1
\
- Title 2
\
- Title 3
\
- Title 4
"
+ "title
"
);
```
@@ -64,6 +73,14 @@ The following code demonstrates how to add a id for every headline and return
own error type while rendering.
```rust
+use std::convert::From;
+use std::io::{Error as IOError, Write};
+use std::string::FromUtf8Error;
+
+use orgize::export::{html::Escape, DefaultHtmlHandler, HtmlHandler};
+use orgize::{Element, Org};
+use slugify::slugify;
+
#[derive(Debug)]
enum MyError {
IO(IOError),
@@ -112,23 +129,13 @@ impl HtmlHandler for MyHtmlHandler {
}
fn main() -> Result<(), MyError> {
- let contents = r"* Title 1
-*Section 1*
-** Title 2
-_Section 2_
-* Title 3
-/Section 3/
-* Title 4
-=Section 4=";
-
let mut writer = Vec::new();
- Org::parse(&contents).html(&mut writer, MyHtmlHandler)?;
+ Org::parse("* title\n*section*").html(&mut writer, MyHtmlHandler)?;
+
assert_eq!(
String::from_utf8(writer)?,
- "\
- \
- \
- "
+ "\
+ "
);
Ok(())
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..5b829d8
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,19 @@
+//! Parse configuration module
+
+/// 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],
+}
+
+impl Default for ParseConfig<'_> {
+ fn default() -> Self {
+ ParseConfig {
+ default_todo_keywords: &["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"],
+ todo_keywords: &[],
+ }
+ }
+}
diff --git a/src/elements/headline.rs b/src/elements/headline.rs
index 8717baa..ea74e1b 100644
--- a/src/elements/headline.rs
+++ b/src/elements/headline.rs
@@ -3,8 +3,7 @@
use jetscii::ByteSubstring;
use memchr::{memchr, memchr2, memrchr};
-pub const DEFAULT_TODO_KEYWORDS: &[&str] =
- &["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"];
+use crate::config::ParseConfig;
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
@@ -26,7 +25,10 @@ pub struct Headline<'a> {
}
impl Headline<'_> {
- pub(crate) fn parse<'a>(text: &'a str, keywords: &[&str]) -> (Headline<'a>, usize, usize) {
+ pub(crate) fn parse<'a>(
+ text: &'a str,
+ config: &ParseConfig<'_>,
+ ) -> (Headline<'a>, usize, usize) {
let level = memchr2(b'\n', b' ', text.as_bytes()).unwrap_or_else(|| text.len());
debug_assert!(level > 0);
@@ -65,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 keywords.contains(&word) {
+ if config.todo_keywords.contains(&word) || config.default_todo_keywords.contains(&word)
+ {
(Some(word), &tail[off..])
} else {
(None, tail)
@@ -149,7 +152,11 @@ impl Headline<'_> {
#[test]
fn parse() {
assert_eq!(
- Headline::parse("**** DONE [#A] COMMENT Title :tag:a2%:", &["DONE"]).0,
+ Headline::parse(
+ "**** DONE [#A] COMMENT Title :tag:a2%:",
+ &ParseConfig::default()
+ )
+ .0,
Headline {
level: 4,
priority: Some('A'),
@@ -159,7 +166,11 @@ fn parse() {
},
);
assert_eq!(
- Headline::parse("**** ToDO [#A] COMMENT Title :tag:a2%:", &["DONE"]).0,
+ Headline::parse(
+ "**** ToDO [#A] COMMENT Title :tag:a2%:",
+ &ParseConfig::default()
+ )
+ .0,
Headline {
level: 4,
priority: None,
@@ -169,7 +180,11 @@ fn parse() {
},
);
assert_eq!(
- Headline::parse("**** T0DO [#A] COMMENT Title :tag:a2%:", &["DONE"]).0,
+ Headline::parse(
+ "**** T0DO [#A] COMMENT Title :tag:a2%:",
+ &ParseConfig::default()
+ )
+ .0,
Headline {
level: 4,
priority: None,
@@ -179,7 +194,11 @@ fn parse() {
},
);
assert_eq!(
- Headline::parse("**** DONE [#1] COMMENT Title :tag:a2%:", &["DONE"]).0,
+ Headline::parse(
+ "**** DONE [#1] COMMENT Title :tag:a2%:",
+ &ParseConfig::default()
+ )
+ .0,
Headline {
level: 4,
priority: None,
@@ -189,7 +208,11 @@ fn parse() {
},
);
assert_eq!(
- Headline::parse("**** DONE [#a] COMMENT Title :tag:a2%:", &["DONE"]).0,
+ Headline::parse(
+ "**** DONE [#a] COMMENT Title :tag:a2%:",
+ &ParseConfig::default()
+ )
+ .0,
Headline {
level: 4,
priority: None,
@@ -199,7 +222,11 @@ fn parse() {
},
);
assert_eq!(
- Headline::parse("**** DONE [#A] COMMENT Title :tag:a2%", &["DONE"]).0,
+ Headline::parse(
+ "**** DONE [#A] COMMENT Title :tag:a2%",
+ &ParseConfig::default()
+ )
+ .0,
Headline {
level: 4,
priority: Some('A'),
@@ -209,7 +236,11 @@ fn parse() {
},
);
assert_eq!(
- Headline::parse("**** DONE [#A] COMMENT Title tag:a2%:", &["DONE"]).0,
+ Headline::parse(
+ "**** DONE [#A] COMMENT Title tag:a2%:",
+ &ParseConfig::default()
+ )
+ .0,
Headline {
level: 4,
priority: Some('A'),
@@ -219,7 +250,7 @@ fn parse() {
},
);
assert_eq!(
- Headline::parse("**** COMMENT Title tag:a2%:", &["DONE"]).0,
+ Headline::parse("**** COMMENT Title tag:a2%:", &ParseConfig::default()).0,
Headline {
level: 4,
priority: None,
@@ -233,7 +264,14 @@ fn parse() {
#[test]
fn parse_todo_keywords() {
assert_eq!(
- Headline::parse("**** DONE [#A] COMMENT Title :tag:a2%:", &[]).0,
+ Headline::parse(
+ "**** DONE [#A] COMMENT Title :tag:a2%:",
+ &ParseConfig {
+ default_todo_keywords: &[],
+ ..Default::default()
+ }
+ )
+ .0,
Headline {
level: 4,
priority: None,
@@ -243,7 +281,14 @@ fn parse_todo_keywords() {
},
);
assert_eq!(
- Headline::parse("**** TASK [#A] COMMENT Title :tag:a2%:", &["TASK"]).0,
+ Headline::parse(
+ "**** TASK [#A] COMMENT Title :tag:a2%:",
+ &ParseConfig {
+ todo_keywords: &["TASK"],
+ ..Default::default()
+ }
+ )
+ .0,
Headline {
level: 4,
priority: Some('A'),
@@ -256,21 +301,55 @@ fn parse_todo_keywords() {
#[test]
fn is_commented() {
- assert!(Headline::parse("* COMMENT Title", &[]).0.is_commented());
- assert!(!Headline::parse("* Title", &[]).0.is_commented());
- assert!(!Headline::parse("* C0MMENT Title", &[]).0.is_commented());
- assert!(!Headline::parse("* comment Title", &[]).0.is_commented());
+ assert!(Headline::parse("* COMMENT Title", &ParseConfig::default())
+ .0
+ .is_commented());
+ assert!(!Headline::parse("* Title", &ParseConfig::default())
+ .0
+ .is_commented());
+ assert!(!Headline::parse("* C0MMENT Title", &ParseConfig::default())
+ .0
+ .is_commented());
+ assert!(!Headline::parse("* comment Title", &ParseConfig::default())
+ .0
+ .is_commented());
}
#[test]
fn is_archived() {
- assert!(Headline::parse("* Title :ARCHIVE:", &[]).0.is_archived());
- assert!(Headline::parse("* Title :t:ARCHIVE:", &[]).0.is_archived());
- assert!(Headline::parse("* Title :ARCHIVE:t:", &[]).0.is_archived());
- assert!(!Headline::parse("* Title", &[]).0.is_commented());
- assert!(!Headline::parse("* Title :ARCHIVED:", &[]).0.is_archived());
- assert!(!Headline::parse("* Title :ARCHIVES:", &[]).0.is_archived());
- assert!(!Headline::parse("* Title :archive:", &[]).0.is_archived());
+ assert!(
+ Headline::parse("* Title :ARCHIVE:", &ParseConfig::default())
+ .0
+ .is_archived()
+ );
+ assert!(
+ Headline::parse("* Title :t:ARCHIVE:", &ParseConfig::default())
+ .0
+ .is_archived()
+ );
+ assert!(
+ Headline::parse("* Title :ARCHIVE:t:", &ParseConfig::default())
+ .0
+ .is_archived()
+ );
+ assert!(!Headline::parse("* Title", &ParseConfig::default())
+ .0
+ .is_commented());
+ assert!(
+ !Headline::parse("* Title :ARCHIVED:", &ParseConfig::default())
+ .0
+ .is_archived()
+ );
+ assert!(
+ !Headline::parse("* Title :ARCHIVES:", &ParseConfig::default())
+ .0
+ .is_archived()
+ );
+ assert!(
+ !Headline::parse("* Title :archive:", &ParseConfig::default())
+ .0
+ .is_archived()
+ );
}
#[test]
diff --git a/src/elements/mod.rs b/src/elements/mod.rs
index 9044599..3b71b81 100644
--- a/src/elements/mod.rs
+++ b/src/elements/mod.rs
@@ -32,7 +32,7 @@ pub use self::{
dyn_block::DynBlock,
fn_def::FnDef,
fn_ref::FnRef,
- headline::{Headline, DEFAULT_TODO_KEYWORDS},
+ headline::Headline,
inline_call::InlineCall,
inline_src::InlineSrc,
keyword::{BabelCall, Keyword},
diff --git a/src/lib.rs b/src/lib.rs
index 724ee86..196c506 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,14 +9,24 @@
//! ```rust
//! use orgize::Org;
//!
-//! let org = Org::parse(r#"* Title 1
-//! *Section 1*
-//! ** Title 2
-//! _Section 2_
-//! * Title 3
-//! /Section 3/
-//! * Title 4
-//! =Section 4="#);
+//! Org::parse("* DONE Title :tag:");
+//! ```
+//!
+//! or [`Org::parse_with_config`]:
+//!
+//! [`Org::parse_with_config`]: org/struct.Org.html#method.parse_with_config
+//!
+//! ``` rust
+//! use orgize::{Org, ParseConfig};
+//!
+//! Org::parse_with_config(
+//! "* TASK Title 1",
+//! ParseConfig {
+//! // custom todo keywords
+//! todo_keywords: &["TASK"],
+//! ..Default::default()
+//! },
+//! );
//! ```
//!
//! # Iter
@@ -29,18 +39,9 @@
//! [`Element`]: elements/enum.Element.html
//!
//! ```rust
-//! # use orgize::Org;
-//! #
-//! # let org = Org::parse(r#"* Title 1
-//! # *Section 1*
-//! # ** Title 2
-//! # _Section 2_
-//! # * Title 3
-//! # /Section 3/
-//! # * Title 4
-//! # =Section 4="#);
-//! #
-//! for event in org.iter() {
+//! use orgize::Org;
+//!
+//! for event in Org::parse("* DONE Title :tag:").iter() {
//! // handling the event
//! }
//! ```
@@ -60,26 +61,14 @@
//! [`DefaultHtmlHandler`]: export/html/struct.DefaultHtmlHandler.html
//!
//! ```rust
-//! # use orgize::Org;
-//! #
-//! # let org = Org::parse(r#"* Title 1
-//! # *Section 1*
-//! # ** Title 2
-//! # _Section 2_
-//! # * Title 3
-//! # /Section 3/
-//! # * Title 4
-//! # =Section 4="#);
-//! #
+//! use orgize::Org;
+//!
//! let mut writer = Vec::new();
-//! org.html_default(&mut writer).unwrap();
+//! Org::parse("* title\n*section*").html_default(&mut writer).unwrap();
//!
//! assert_eq!(
//! String::from_utf8(writer).unwrap(),
-//! "Title 1
\
-//! Title 2
\
-//! Title 3
\
-//! Title 4
"
+//! "title
"
//! );
//! ```
//!
@@ -95,14 +84,14 @@
//! own error type while rendering.
//!
//! ```rust
-//! # use std::convert::From;
-//! # use std::io::{Error as IOError, Write};
-//! # use std::string::FromUtf8Error;
-//! #
-//! # use orgize::export::{html::Escape, DefaultHtmlHandler, HtmlHandler};
-//! # use orgize::{Element, Org};
-//! # use slugify::slugify;
-//! #
+//! use std::convert::From;
+//! use std::io::{Error as IOError, Write};
+//! use std::string::FromUtf8Error;
+//!
+//! use orgize::export::{html::Escape, DefaultHtmlHandler, HtmlHandler};
+//! use orgize::{Element, Org};
+//! use slugify::slugify;
+//!
//! #[derive(Debug)]
//! enum MyError {
//! IO(IOError),
@@ -151,23 +140,13 @@
//! }
//!
//! fn main() -> Result<(), MyError> {
-//! let contents = r"* Title 1
-//! *Section 1*
-//! ** Title 2
-//! _Section 2_
-//! * Title 3
-//! /Section 3/
-//! * Title 4
-//! =Section 4=";
-//!
//! let mut writer = Vec::new();
-//! Org::parse(&contents).html(&mut writer, MyHtmlHandler)?;
+//! Org::parse("* title\n*section*").html(&mut writer, MyHtmlHandler)?;
+//!
//! assert_eq!(
//! String::from_utf8(writer)?,
-//! "\
-//! \
-//! \
-//! "
+//! "\
+//! "
//! );
//!
//! Ok(())
@@ -230,6 +209,7 @@
//!
//! MIT
+pub mod config;
pub mod elements;
pub mod export;
pub mod iter;
@@ -237,6 +217,7 @@ pub mod org;
#[cfg(feature = "serde")]
mod serde;
+pub use config::ParseConfig;
pub use elements::Element;
pub use iter::{Event, Iter};
pub use org::Org;
diff --git a/src/org.rs b/src/org.rs
index b31f342..5ed9c83 100644
--- a/src/org.rs
+++ b/src/org.rs
@@ -3,6 +3,7 @@ use jetscii::bytes;
use memchr::{memchr, memchr_iter};
use std::io::{Error, Write};
+use crate::config::ParseConfig;
use crate::elements::*;
use crate::export::{DefaultHtmlHandler, HtmlHandler};
use crate::iter::Iter;
@@ -28,7 +29,27 @@ impl<'a> Org<'a> {
document,
text,
};
- org.parse_internal();
+
+ org.parse_internal(ParseConfig::default());
+
+ org
+ }
+
+ pub fn parse_with_config(text: &'a str, config: ParseConfig<'_>) -> Self {
+ let mut arena = Arena::new();
+ let document = arena.new_node(Element::Document {
+ begin: 0,
+ end: text.len(),
+ contents_begin: 0,
+ contents_end: text.len(),
+ });
+
+ let mut org = Org {
+ arena,
+ document,
+ text,
+ };
+ org.parse_internal(config);
org
}
@@ -62,7 +83,7 @@ impl<'a> Org<'a> {
self.html(wrtier, DefaultHtmlHandler)
}
- fn parse_internal(&mut self) {
+ fn parse_internal(&mut self, config: ParseConfig<'_>) {
let mut node = self.document;
loop {
match self.arena[node].data {
@@ -92,7 +113,7 @@ impl<'a> Org<'a> {
}
}
while begin < end {
- let (headline, off, end) = Headline::parse(&self.text[begin..end], &[]);
+ let (headline, off, end) = Headline::parse(&self.text[begin..end], &config);
let headline = Element::Headline {
headline,
begin,