feat(elements): wrap up every &str with Cow

This commit is contained in:
PoiScript 2019-08-06 21:10:57 +08:00
parent 54c063f41b
commit a1af0663b5
25 changed files with 450 additions and 237 deletions

View file

@ -42,7 +42,7 @@ impl HtmlHandler<MyError> for MyHtmlHandler {
w, w,
"<h{0}><a id=\"{1}\" href=\"#{1}\">", "<h{0}><a id=\"{1}\" href=\"#{1}\">",
title.level, title.level,
slugify!(title.raw), slugify!(&title.raw),
)?; )?;
} }
} }

View file

@ -46,7 +46,7 @@ impl HtmlHandler<Error> for SyntectHtmlHandler {
Element::InlineSrc(inline_src) => write!( Element::InlineSrc(inline_src) => write!(
w, w,
"<code>{}</code>", "<code>{}</code>",
self.highlight(Some(inline_src.lang), inline_src.body) self.highlight(Some(&inline_src.lang), &inline_src.body)
)?, )?,
Element::SourceBlock(block) => { Element::SourceBlock(block) => {
if block.language.is_empty() { if block.language.is_empty() {
@ -56,7 +56,7 @@ impl HtmlHandler<Error> for SyntectHtmlHandler {
w, w,
"<div class=\"org-src-container\"><pre class=\"src src-{}\">{}</pre></div>", "<div class=\"org-src-container\"><pre class=\"src src-{}\">{}</pre></div>",
block.language, block.language,
self.highlight(Some(block.language), block.contents) self.highlight(Some(&block.language), &block.contents)
)? )?
} }
} }
@ -68,7 +68,7 @@ impl HtmlHandler<Error> for SyntectHtmlHandler {
Element::ExampleBlock(block) => write!( Element::ExampleBlock(block) => write!(
w, w,
"<pre class=\"example\">{}</pre>", "<pre class=\"example\">{}</pre>",
self.highlight(None, block.contents) self.highlight(None, &block.contents)
)?, )?,
_ => self.default_hanlder.start(w, element)?, _ => self.default_hanlder.start(w, element)?,
} }

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{bytes::complete::tag_no_case, character::complete::alpha1, sequence::preceded, IResult}; use nom::{bytes::complete::tag_no_case, character::complete::alpha1, sequence::preceded, IResult};
use crate::parsers::{take_lines_till, take_until_eol}; use crate::parsers::{take_lines_till, take_until_eol};
@ -5,8 +7,8 @@ use crate::parsers::{take_lines_till, take_until_eol};
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)] #[derive(Debug)]
pub struct Block<'a> { pub struct Block<'a> {
pub name: &'a str, pub name: Cow<'a, str>,
pub args: Option<&'a str>, pub args: Option<Cow<'a, str>>,
} }
impl Block<'_> { impl Block<'_> {
@ -22,8 +24,12 @@ impl Block<'_> {
input, input,
( (
Block { Block {
name, name: name.into(),
args: if args.is_empty() { None } else { Some(args) }, args: if args.is_empty() {
None
} else {
Some(args.into())
},
}, },
contents, contents,
), ),
@ -39,7 +45,7 @@ fn parse() {
"", "",
( (
Block { Block {
name: "SRC", name: "SRC".into(),
args: None, args: None,
}, },
"" ""
@ -52,8 +58,8 @@ fn parse() {
"", "",
( (
Block { Block {
name: "SRC", name: "SRC".into(),
args: Some("javascript"), args: Some("javascript".into()),
}, },
"console.log('Hello World!');\n" "console.log('Hello World!');\n"
) )
@ -66,60 +72,60 @@ fn parse() {
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct SpecialBlock<'a> { pub struct SpecialBlock<'a> {
pub parameters: Option<&'a str>, pub parameters: Option<Cow<'a, str>>,
pub name: &'a str, pub name: Cow<'a, str>,
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct QuoteBlock<'a> { pub struct QuoteBlock<'a> {
pub parameters: Option<&'a str>, pub parameters: Option<Cow<'a, str>>,
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct CenterBlock<'a> { pub struct CenterBlock<'a> {
pub parameters: Option<&'a str>, pub parameters: Option<Cow<'a, str>>,
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct VerseBlock<'a> { pub struct VerseBlock<'a> {
pub parameters: Option<&'a str>, pub parameters: Option<Cow<'a, str>>,
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct CommentBlock<'a> { pub struct CommentBlock<'a> {
pub data: Option<&'a str>, pub data: Option<Cow<'a, str>>,
pub contents: &'a str, pub contents: Cow<'a, str>,
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct ExampleBlock<'a> { pub struct ExampleBlock<'a> {
pub data: Option<&'a str>, pub data: Option<Cow<'a, str>>,
pub contents: &'a str, pub contents: Cow<'a, str>,
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct ExportBlock<'a> { pub struct ExportBlock<'a> {
pub data: &'a str, pub data: Cow<'a, str>,
pub contents: &'a str, pub contents: Cow<'a, str>,
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct SourceBlock<'a> { pub struct SourceBlock<'a> {
pub contents: &'a str, pub contents: Cow<'a, str>,
pub language: &'a str, pub language: Cow<'a, str>,
pub arguments: &'a str, pub arguments: Cow<'a, str>,
} }

View file

@ -1,7 +1,9 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::tag, bytes::complete::tag,
character::complete::{char, digit1, space0}, character::complete::{char, digit1, space0},
combinator::{peek, recognize}, combinator::recognize,
sequence::separated_pair, sequence::separated_pair,
IResult, IResult,
}; };
@ -22,18 +24,18 @@ pub enum Clock<'a> {
start: Datetime<'a>, start: Datetime<'a>,
end: Datetime<'a>, end: Datetime<'a>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
repeater: Option<&'a str>, repeater: Option<Cow<'a, str>>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
delay: Option<&'a str>, delay: Option<Cow<'a, str>>,
duration: &'a str, duration: Cow<'a, str>,
}, },
/// running Clock /// running Clock
Running { Running {
start: Datetime<'a>, start: Datetime<'a>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
repeater: Option<&'a str>, repeater: Option<Cow<'a, str>>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
delay: Option<&'a str>, delay: Option<Cow<'a, str>>,
}, },
} }
@ -41,7 +43,6 @@ impl Clock<'_> {
pub(crate) fn parse(input: &str) -> IResult<&str, Element<'_>> { pub(crate) fn parse(input: &str) -> IResult<&str, Element<'_>> {
let (input, _) = tag("CLOCK:")(input)?; let (input, _) = tag("CLOCK:")(input)?;
let (input, _) = space0(input)?; let (input, _) = space0(input)?;
let (input, _) = peek(tag("["))(input)?;
let (input, timestamp) = Timestamp::parse_inactive(input)?; let (input, timestamp) = Timestamp::parse_inactive(input)?;
match timestamp { match timestamp {
@ -64,7 +65,7 @@ impl Clock<'_> {
end, end,
repeater, repeater,
delay, delay,
duration, duration: duration.into(),
}), }),
)) ))
} }
@ -83,7 +84,9 @@ impl Clock<'_> {
}), }),
)) ))
} }
_ => unreachable!(), _ => unreachable!(
"`Timestamp::parse_inactive` only returns `Timestamp::InactiveRange` or `Timestamp::Inactive`."
),
} }
} }
@ -150,7 +153,7 @@ fn parse() {
year: 2003, year: 2003,
month: 9, month: 9,
day: 16, day: 16,
dayname: "Tue", dayname: "Tue".into(),
hour: Some(9), hour: Some(9),
minute: Some(39) minute: Some(39)
}, },
@ -168,7 +171,7 @@ fn parse() {
year: 2003, year: 2003,
month: 9, month: 9,
day: 16, day: 16,
dayname: "Tue", dayname: "Tue".into(),
hour: Some(9), hour: Some(9),
minute: Some(39) minute: Some(39)
}, },
@ -176,13 +179,13 @@ fn parse() {
year: 2003, year: 2003,
month: 9, month: 9,
day: 16, day: 16,
dayname: "Tue", dayname: "Tue".into(),
hour: Some(10), hour: Some(10),
minute: Some(39) minute: Some(39)
}, },
repeater: None, repeater: None,
delay: None, delay: None,
duration: "1:00", duration: "1:00".into(),
}) })
)) ))
); );

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::complete::tag, bytes::complete::tag,
@ -11,7 +13,7 @@ use nom::{
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct Cookie<'a> { pub struct Cookie<'a> {
pub value: &'a str, pub value: Cow<'a, str>,
} }
impl Cookie<'_> { impl Cookie<'_> {
@ -26,7 +28,12 @@ impl Cookie<'_> {
tag("]"), tag("]"),
))(input)?; ))(input)?;
Ok((input, Cookie { value })) Ok((
input,
Cookie {
value: value.into(),
},
))
} }
} }
@ -34,22 +41,66 @@ impl Cookie<'_> {
fn parse() { fn parse() {
assert_eq!( assert_eq!(
Cookie::parse("[1/10]"), Cookie::parse("[1/10]"),
Ok(("", Cookie { value: "[1/10]" })) Ok((
"",
Cookie {
value: "[1/10]".into()
}
))
); );
assert_eq!( assert_eq!(
Cookie::parse("[1/1000]"), Cookie::parse("[1/1000]"),
Ok(("", Cookie { value: "[1/1000]" })) Ok((
"",
Cookie {
value: "[1/1000]".into()
}
))
);
assert_eq!(
Cookie::parse("[10%]"),
Ok((
"",
Cookie {
value: "[10%]".into()
}
))
);
assert_eq!(
Cookie::parse("[%]"),
Ok((
"",
Cookie {
value: "[%]".into()
}
))
);
assert_eq!(
Cookie::parse("[/]"),
Ok((
"",
Cookie {
value: "[/]".into()
}
))
); );
assert_eq!(Cookie::parse("[10%]"), Ok(("", Cookie { value: "[10%]" })));
assert_eq!(Cookie::parse("[%]"), Ok(("", Cookie { value: "[%]" })));
assert_eq!(Cookie::parse("[/]"), Ok(("", Cookie { value: "[/]" })));
assert_eq!( assert_eq!(
Cookie::parse("[100/]"), Cookie::parse("[100/]"),
Ok(("", Cookie { value: "[100/]" })) Ok((
"",
Cookie {
value: "[100/]".into()
}
))
); );
assert_eq!( assert_eq!(
Cookie::parse("[/100]"), Cookie::parse("[/100]"),
Ok(("", Cookie { value: "[/100]" })) Ok((
"",
Cookie {
value: "[/100]".into()
}
))
); );
assert!(Cookie::parse("[10% ]").is_err()); assert!(Cookie::parse("[10% ]").is_err());

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use crate::parsers::{eol, take_lines_till}; use crate::parsers::{eol, take_lines_till};
use nom::{ use nom::{
@ -10,7 +12,7 @@ use nom::{
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct Drawer<'a> { pub struct Drawer<'a> {
pub name: &'a str, pub name: Cow<'a, str>,
} }
impl Drawer<'_> { impl Drawer<'_> {
@ -24,7 +26,7 @@ impl Drawer<'_> {
let (input, _) = eol(input)?; let (input, _) = eol(input)?;
let (input, contents) = take_lines_till(|line| line.eq_ignore_ascii_case(":END:"))(input)?; let (input, contents) = take_lines_till(|line| line.eq_ignore_ascii_case(":END:"))(input)?;
Ok((input, (Drawer { name }, contents))) Ok((input, (Drawer { name: name.into() }, contents)))
} }
} }
@ -32,6 +34,14 @@ impl Drawer<'_> {
fn parse() { fn parse() {
assert_eq!( assert_eq!(
Drawer::parse(":PROPERTIES:\n :CUSTOM_ID: id\n :END:"), Drawer::parse(":PROPERTIES:\n :CUSTOM_ID: id\n :END:"),
Ok(("", (Drawer { name: "PROPERTIES" }, " :CUSTOM_ID: id\n"))) Ok((
"",
(
Drawer {
name: "PROPERTIES".into()
},
" :CUSTOM_ID: id\n"
)
))
) )
} }

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use crate::elements::Element; use crate::elements::Element;
use crate::parsers::{take_lines_till, take_until_eol}; use crate::parsers::{take_lines_till, take_until_eol};
@ -11,9 +13,9 @@ use nom::{
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct DynBlock<'a> { pub struct DynBlock<'a> {
pub block_name: &'a str, pub block_name: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub arguments: Option<&'a str>, pub arguments: Option<Cow<'a, str>>,
} }
impl DynBlock<'_> { impl DynBlock<'_> {
@ -30,8 +32,12 @@ impl DynBlock<'_> {
input, input,
( (
Element::DynBlock(DynBlock { Element::DynBlock(DynBlock {
block_name: name, block_name: name.into(),
arguments: if args.is_empty() { None } else { Some(args) }, arguments: if args.is_empty() {
None
} else {
Some(args.into())
},
}), }),
contents, contents,
), ),
@ -48,8 +54,8 @@ fn parse() {
"", "",
( (
Element::DynBlock(DynBlock { Element::DynBlock(DynBlock {
block_name: "clocktable", block_name: "clocktable".into(),
arguments: Some(":scope file"), arguments: Some(":scope file".into()),
}), }),
"CONTENTS\n" "CONTENTS\n"
) )

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use memchr::memchr; use memchr::memchr;
use nom::{ use nom::{
bytes::complete::{tag, take_while1}, bytes::complete::{tag, take_while1},
@ -9,7 +11,7 @@ use nom::{
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct FnDef<'a> { pub struct FnDef<'a> {
pub label: &'a str, pub label: Cow<'a, str>,
} }
fn parse_label(input: &str) -> IResult<&str, &str> { fn parse_label(input: &str) -> IResult<&str, &str> {
@ -29,7 +31,13 @@ impl FnDef<'_> {
let end = memchr(b'\n', tail.as_bytes()).unwrap_or_else(|| tail.len()); let end = memchr(b'\n', tail.as_bytes()).unwrap_or_else(|| tail.len());
Some((&tail[end..], FnDef { label }, &tail[0..end])) Some((
&tail[end..],
FnDef {
label: label.into(),
},
&tail[0..end],
))
} }
} }
@ -37,19 +45,37 @@ impl FnDef<'_> {
fn parse() { fn parse() {
assert_eq!( assert_eq!(
FnDef::parse("[fn:1] https://orgmode.org"), FnDef::parse("[fn:1] https://orgmode.org"),
Some(("", FnDef { label: "1" }, " https://orgmode.org")) Some(("", FnDef { label: "1".into() }, " https://orgmode.org"))
); );
assert_eq!( assert_eq!(
FnDef::parse("[fn:word_1] https://orgmode.org"), FnDef::parse("[fn:word_1] https://orgmode.org"),
Some(("", FnDef { label: "word_1" }, " https://orgmode.org")) Some((
"",
FnDef {
label: "word_1".into()
},
" https://orgmode.org"
))
); );
assert_eq!( assert_eq!(
FnDef::parse("[fn:WORD-1] https://orgmode.org"), FnDef::parse("[fn:WORD-1] https://orgmode.org"),
Some(("", FnDef { label: "WORD-1" }, " https://orgmode.org")) Some((
"",
FnDef {
label: "WORD-1".into()
},
" https://orgmode.org"
))
); );
assert_eq!( assert_eq!(
FnDef::parse("[fn:WORD]"), FnDef::parse("[fn:WORD]"),
Some(("", FnDef { label: "WORD" }, "")) Some((
"",
FnDef {
label: "WORD".into()
},
""
))
); );
assert_eq!(FnDef::parse("[fn:] https://orgmode.org"), None); assert_eq!(FnDef::parse("[fn:] https://orgmode.org"), None);
assert_eq!(FnDef::parse("[fn:wor d] https://orgmode.org"), None); assert_eq!(FnDef::parse("[fn:wor d] https://orgmode.org"), None);

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use memchr::memchr2_iter; use memchr::memchr2_iter;
use nom::{ use nom::{
bytes::complete::{tag, take_while}, bytes::complete::{tag, take_while},
@ -12,9 +14,9 @@ use nom::{
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct FnRef<'a> { pub struct FnRef<'a> {
pub label: &'a str, pub label: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub definition: Option<&'a str>, pub definition: Option<Cow<'a, str>>,
} }
fn balanced_brackets(input: &str) -> IResult<&str, &str> { fn balanced_brackets(input: &str) -> IResult<&str, &str> {
@ -40,7 +42,13 @@ impl FnRef<'_> {
let (input, definition) = opt(preceded(tag(":"), balanced_brackets))(input)?; let (input, definition) = opt(preceded(tag(":"), balanced_brackets))(input)?;
let (input, _) = tag("]")(input)?; let (input, _) = tag("]")(input)?;
Ok((input, FnRef { label, definition })) Ok((
input,
FnRef {
label: label.into(),
definition: definition.map(Into::into),
},
))
} }
} }
@ -51,7 +59,7 @@ fn parse() {
Ok(( Ok((
"", "",
FnRef { FnRef {
label: "1", label: "1".into(),
definition: None definition: None
}, },
)) ))
@ -61,8 +69,8 @@ fn parse() {
Ok(( Ok((
"", "",
FnRef { FnRef {
label: "1", label: "1".into(),
definition: Some("2") definition: Some("2".into())
}, },
)) ))
); );
@ -71,8 +79,8 @@ fn parse() {
Ok(( Ok((
"", "",
FnRef { FnRef {
label: "", label: "".into(),
definition: Some("2") definition: Some("2".into())
}, },
)) ))
); );
@ -81,8 +89,8 @@ fn parse() {
Ok(( Ok((
"", "",
FnRef { FnRef {
label: "", label: "".into(),
definition: Some("[]") definition: Some("[]".into())
}, },
)) ))
); );

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::{tag, take_till}, bytes::complete::{tag, take_till},
combinator::opt, combinator::opt,
@ -11,12 +13,12 @@ use crate::elements::Element;
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct InlineCall<'a> { pub struct InlineCall<'a> {
pub name: &'a str, pub name: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub inside_header: Option<&'a str>, pub inside_header: Option<Cow<'a, str>>,
pub arguments: &'a str, pub arguments: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub end_header: Option<&'a str>, pub end_header: Option<Cow<'a, str>>,
} }
impl<'a> InlineCall<'a> { impl<'a> InlineCall<'a> {
@ -42,10 +44,10 @@ impl<'a> InlineCall<'a> {
Ok(( Ok((
input, input,
Element::InlineCall(InlineCall { Element::InlineCall(InlineCall {
name, name: name.into(),
arguments, arguments: arguments.into(),
inside_header, inside_header: inside_header.map(Into::into),
end_header, end_header: end_header.map(Into::into),
}), }),
)) ))
} }
@ -58,8 +60,8 @@ fn parse() {
Ok(( Ok((
"", "",
Element::InlineCall(InlineCall { Element::InlineCall(InlineCall {
name: "square", name: "square".into(),
arguments: "4", arguments: "4".into(),
inside_header: None, inside_header: None,
end_header: None, end_header: None,
}), }),
@ -70,9 +72,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::InlineCall(InlineCall { Element::InlineCall(InlineCall {
name: "square", name: "square".into(),
arguments: "4", arguments: "4".into(),
inside_header: Some(":results output"), inside_header: Some(":results output".into()),
end_header: None, end_header: None,
}), }),
)) ))
@ -82,10 +84,10 @@ fn parse() {
Ok(( Ok((
"", "",
Element::InlineCall(InlineCall { Element::InlineCall(InlineCall {
name: "square", name: "square".into(),
arguments: "4", arguments: "4".into(),
inside_header: None, inside_header: None,
end_header: Some(":results html"), end_header: Some(":results html".into()),
}), }),
)) ))
); );
@ -94,10 +96,10 @@ fn parse() {
Ok(( Ok((
"", "",
Element::InlineCall(InlineCall { Element::InlineCall(InlineCall {
name: "square", name: "square".into(),
arguments: "4", arguments: "4".into(),
inside_header: Some(":results output"), inside_header: Some(":results output".into()),
end_header: Some(":results html"), end_header: Some(":results html".into()),
}), }),
)) ))
); );

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::{tag, take_till, take_while1}, bytes::complete::{tag, take_till, take_while1},
combinator::opt, combinator::opt,
@ -11,10 +13,10 @@ use crate::elements::Element;
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct InlineSrc<'a> { pub struct InlineSrc<'a> {
pub lang: &'a str, pub lang: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub options: Option<&'a str>, pub options: Option<Cow<'a, str>>,
pub body: &'a str, pub body: Cow<'a, str>,
} }
impl InlineSrc<'_> { impl InlineSrc<'_> {
@ -34,9 +36,9 @@ impl InlineSrc<'_> {
Ok(( Ok((
input, input,
Element::InlineSrc(InlineSrc { Element::InlineSrc(InlineSrc {
lang, lang: lang.into(),
options, options: options.map(Into::into),
body, body: body.into(),
}), }),
)) ))
} }
@ -49,9 +51,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::InlineSrc(InlineSrc { Element::InlineSrc(InlineSrc {
lang: "C", lang: "C".into(),
options: None, options: None,
body: "int a = 0;" body: "int a = 0;".into()
}), }),
)) ))
); );
@ -60,9 +62,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::InlineSrc(InlineSrc { Element::InlineSrc(InlineSrc {
lang: "xml", lang: "xml".into(),
options: Some(":exports code"), options: Some(":exports code".into()),
body: "<tag>text</tag>", body: "<tag>text</tag>".into(),
}), }),
)) ))
); );

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::{tag, take_till}, bytes::complete::{tag, take_till},
combinator::opt, combinator::opt,
@ -12,17 +14,17 @@ use crate::parsers::take_until_eol;
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct Keyword<'a> { pub struct Keyword<'a> {
pub key: &'a str, pub key: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub optional: Option<&'a str>, pub optional: Option<Cow<'a, str>>,
pub value: &'a str, pub value: Cow<'a, str>,
} }
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct BabelCall<'a> { pub struct BabelCall<'a> {
pub value: &'a str, pub value: Cow<'a, str>,
} }
impl Keyword<'_> { impl Keyword<'_> {
@ -40,14 +42,19 @@ impl Keyword<'_> {
let (input, value) = take_until_eol(input)?; let (input, value) = take_until_eol(input)?;
if key.eq_ignore_ascii_case("CALL") { if key.eq_ignore_ascii_case("CALL") {
Ok((input, Element::BabelCall(BabelCall { value }))) Ok((
input,
Element::BabelCall(BabelCall {
value: value.into(),
}),
))
} else { } else {
Ok(( Ok((
input, input,
Element::Keyword(Keyword { Element::Keyword(Keyword {
key, key: key.into(),
optional, optional: optional.map(Into::into),
value, value: value.into(),
}), }),
)) ))
} }
@ -61,9 +68,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Keyword(Keyword { Element::Keyword(Keyword {
key: "KEY", key: "KEY".into(),
optional: None, optional: None,
value: "", value: "".into(),
}) })
)) ))
); );
@ -72,9 +79,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Keyword(Keyword { Element::Keyword(Keyword {
key: "KEY", key: "KEY".into(),
optional: None, optional: None,
value: "VALUE", value: "VALUE".into(),
}) })
)) ))
); );
@ -83,9 +90,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Keyword(Keyword { Element::Keyword(Keyword {
key: "K_E_Y", key: "K_E_Y".into(),
optional: None, optional: None,
value: "VALUE", value: "VALUE".into(),
}) })
)) ))
); );
@ -94,9 +101,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Keyword(Keyword { Element::Keyword(Keyword {
key: "KEY", key: "KEY".into(),
optional: None, optional: None,
value: "VALUE", value: "VALUE".into(),
}) })
)) ))
); );
@ -108,9 +115,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Keyword(Keyword { Element::Keyword(Keyword {
key: "RESULTS", key: "RESULTS".into(),
optional: None, optional: None,
value: "", value: "".into(),
}) })
)) ))
); );
@ -120,9 +127,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Keyword(Keyword { Element::Keyword(Keyword {
key: "ATTR_LATEX", key: "ATTR_LATEX".into(),
optional: None, optional: None,
value: ":width 5cm", value: ":width 5cm".into(),
}) })
)) ))
); );
@ -132,7 +139,7 @@ fn parse() {
Ok(( Ok((
"", "",
Element::BabelCall(BabelCall { Element::BabelCall(BabelCall {
value: "double(n=4)", value: "double(n=4)".into(),
}) })
)) ))
); );
@ -142,9 +149,9 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Keyword(Keyword { Element::Keyword(Keyword {
key: "CAPTION", key: "CAPTION".into(),
optional: Some("Short caption"), optional: Some("Short caption".into()),
value: "Longer caption.", value: "Longer caption.".into(),
}) })
)) ))
); );

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::{tag, take_while}, bytes::complete::{tag, take_while},
combinator::opt, combinator::opt,
@ -11,9 +13,9 @@ use crate::elements::Element;
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct Link<'a> { pub struct Link<'a> {
pub path: &'a str, pub path: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub desc: Option<&'a str>, pub desc: Option<Cow<'a, str>>,
} }
impl Link<'_> { impl Link<'_> {
@ -30,7 +32,13 @@ impl Link<'_> {
tag("]"), tag("]"),
))(input)?; ))(input)?;
let (input, _) = tag("]")(input)?; let (input, _) = tag("]")(input)?;
Ok((input, Element::Link(Link { path, desc }))) Ok((
input,
Element::Link(Link {
path: path.into(),
desc: desc.map(Into::into),
}),
))
} }
} }
@ -41,7 +49,7 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Link(Link { Element::Link(Link {
path: "#id", path: "#id".into(),
desc: None desc: None
},) },)
)) ))
@ -51,8 +59,8 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Link(Link { Element::Link(Link {
path: "#id", path: "#id".into(),
desc: Some("desc") desc: Some("desc".into())
}) })
)) ))
); );

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use memchr::memchr_iter; use memchr::memchr_iter;
use std::iter::once; use std::iter::once;
@ -65,7 +67,7 @@ impl List {
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct ListItem<'a> { pub struct ListItem<'a> {
pub bullet: &'a str, pub bullet: Cow<'a, str>,
} }
impl ListItem<'_> { impl ListItem<'_> {
@ -87,7 +89,7 @@ impl ListItem<'_> {
return ( return (
&text[pos..], &text[pos..],
ListItem { ListItem {
bullet: &text[indent..off], bullet: text[indent..off].into(),
}, },
&text[off..pos], &text[off..pos],
); );
@ -99,7 +101,7 @@ impl ListItem<'_> {
( (
"", "",
ListItem { ListItem {
bullet: &text[indent..off], bullet: text[indent..off].into(),
}, },
&text[off..], &text[off..],
) )

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::{tag, take, take_until, take_while1}, bytes::complete::{tag, take, take_until, take_while1},
combinator::{opt, verify}, combinator::{opt, verify},
@ -11,9 +13,9 @@ use crate::elements::Element;
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct Macros<'a> { pub struct Macros<'a> {
pub name: &'a str, pub name: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub arguments: Option<&'a str>, pub arguments: Option<Cow<'a, str>>,
} }
impl Macros<'_> { impl Macros<'_> {
@ -27,7 +29,13 @@ impl Macros<'_> {
let (input, arguments) = opt(delimited(tag("("), take_until(")}}}"), take(1usize)))(input)?; let (input, arguments) = opt(delimited(tag("("), take_until(")}}}"), take(1usize)))(input)?;
let (input, _) = tag("}}}")(input)?; let (input, _) = tag("}}}")(input)?;
Ok((input, Element::Macros(Macros { name, arguments }))) Ok((
input,
Element::Macros(Macros {
name: name.into(),
arguments: arguments.map(Into::into),
}),
))
} }
} }
@ -38,8 +46,8 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Macros(Macros { Element::Macros(Macros {
name: "poem", name: "poem".into(),
arguments: Some("red,blue") arguments: Some("red,blue".into())
}) })
)) ))
); );
@ -48,8 +56,8 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Macros(Macros { Element::Macros(Macros {
name: "poem", name: "poem".into(),
arguments: Some(")") arguments: Some(")".into())
}) })
)) ))
); );
@ -58,7 +66,7 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Macros(Macros { Element::Macros(Macros {
name: "author", name: "author".into(),
arguments: None arguments: None
}) })
)) ))

View file

@ -80,7 +80,7 @@ fn prase() {
year: 2019, year: 2019,
month: 4, month: 4,
day: 8, day: 8,
dayname: "Mon", dayname: "Mon".into(),
hour: None, hour: None,
minute: None minute: None
}, },

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::{tag, take_until, take_while1}, bytes::complete::{tag, take_until, take_while1},
sequence::{delimited, separated_pair}, sequence::{delimited, separated_pair},
@ -10,8 +12,8 @@ use crate::elements::Element;
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct Snippet<'a> { pub struct Snippet<'a> {
pub name: &'a str, pub name: Cow<'a, str>,
pub value: &'a str, pub value: Cow<'a, str>,
} }
impl Snippet<'_> { impl Snippet<'_> {
@ -27,7 +29,13 @@ impl Snippet<'_> {
tag("@@"), tag("@@"),
)(input)?; )(input)?;
Ok((input, Element::Snippet(Snippet { name, value }))) Ok((
input,
Element::Snippet(Snippet {
name: name.into(),
value: value.into(),
}),
))
} }
} }
@ -38,8 +46,8 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Snippet(Snippet { Element::Snippet(Snippet {
name: "html", name: "html".into(),
value: "<b>" value: "<b>".into()
}) })
)) ))
); );
@ -48,8 +56,8 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Snippet(Snippet { Element::Snippet(Snippet {
name: "latex", name: "latex".into(),
value: "any arbitrary LaTeX code", value: "any arbitrary LaTeX code".into(),
}) })
)) ))
); );
@ -58,8 +66,8 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Snippet(Snippet { Element::Snippet(Snippet {
name: "html", name: "html".into(),
value: "", value: "".into(),
}) })
)) ))
); );
@ -68,8 +76,8 @@ fn parse() {
Ok(( Ok((
"", "",
Element::Snippet(Snippet { Element::Snippet(Snippet {
name: "html", name: "html".into(),
value: "<p>@</p>", value: "<p>@</p>".into(),
}) })
)) ))
); );

View file

@ -1,12 +1,14 @@
use std::borrow::Cow;
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))] #[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[cfg_attr(feature = "ser", serde(tag = "table_type"))] #[cfg_attr(feature = "ser", serde(tag = "table_type"))]
pub enum Table<'a> { pub enum Table<'a> {
#[cfg_attr(feature = "ser", serde(rename = "org"))] #[cfg_attr(feature = "ser", serde(rename = "org"))]
Org { tblfm: Option<&'a str> }, Org { tblfm: Option<Cow<'a, str>> },
#[cfg_attr(feature = "ser", serde(rename = "table.el"))] #[cfg_attr(feature = "ser", serde(rename = "table.el"))]
TableEl { value: &'a str }, TableEl { value: Cow<'a, str> },
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::{tag, take_while}, bytes::complete::{tag, take_while},
combinator::verify, combinator::verify,
@ -11,7 +13,7 @@ use crate::elements::Element;
#[cfg_attr(feature = "ser", derive(serde::Serialize))] #[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)] #[derive(Debug)]
pub struct Target<'a> { pub struct Target<'a> {
pub target: &'a str, pub target: Cow<'a, str>,
} }
impl Target<'_> { impl Target<'_> {
@ -26,7 +28,12 @@ impl Target<'_> {
tag(">>"), tag(">>"),
)(input)?; )(input)?;
Ok((input, Element::Target(Target { target }))) Ok((
input,
Element::Target(Target {
target: target.into(),
}),
))
} }
} }
@ -34,11 +41,21 @@ impl Target<'_> {
fn parse() { fn parse() {
assert_eq!( assert_eq!(
Target::parse("<<target>>"), Target::parse("<<target>>"),
Ok(("", Element::Target(Target { target: "target" }))) Ok((
"",
Element::Target(Target {
target: "target".into()
})
))
); );
assert_eq!( assert_eq!(
Target::parse("<<tar get>>"), Target::parse("<<tar get>>"),
Ok(("", Element::Target(Target { target: "tar get" }))) Ok((
"",
Element::Target(Target {
target: "tar get".into()
})
))
); );
assert!(Target::parse("<<target >>").is_err()); assert!(Target::parse("<<target >>").is_err());
assert!(Target::parse("<< target>>").is_err()); assert!(Target::parse("<< target>>").is_err());

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::{ use nom::{
bytes::complete::{tag, take, take_till, take_while, take_while_m_n}, bytes::complete::{tag, take, take_till, take_while, take_while_m_n},
character::complete::{space0, space1}, character::complete::{space0, space1},
@ -21,7 +23,7 @@ pub struct Datetime<'a> {
pub year: u16, pub year: u16,
pub month: u8, pub month: u8,
pub day: u8, pub day: u8,
pub dayname: &'a str, pub dayname: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub hour: Option<u8>, pub hour: Option<u8>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
@ -64,7 +66,7 @@ fn parse_datetime(input: &str) -> IResult<&str, Datetime<'_>> {
year, year,
month, month,
day, day,
dayname, dayname: dayname.into(),
hour, hour,
minute, minute,
}, },
@ -114,35 +116,35 @@ pub enum Timestamp<'a> {
Active { Active {
start: Datetime<'a>, start: Datetime<'a>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
repeater: Option<&'a str>, repeater: Option<Cow<'a, str>>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
delay: Option<&'a str>, delay: Option<Cow<'a, str>>,
}, },
Inactive { Inactive {
start: Datetime<'a>, start: Datetime<'a>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
repeater: Option<&'a str>, repeater: Option<Cow<'a, str>>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
delay: Option<&'a str>, delay: Option<Cow<'a, str>>,
}, },
ActiveRange { ActiveRange {
start: Datetime<'a>, start: Datetime<'a>,
end: Datetime<'a>, end: Datetime<'a>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
repeater: Option<&'a str>, repeater: Option<Cow<'a, str>>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
delay: Option<&'a str>, delay: Option<Cow<'a, str>>,
}, },
InactiveRange { InactiveRange {
start: Datetime<'a>, start: Datetime<'a>,
end: Datetime<'a>, end: Datetime<'a>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
repeater: Option<&'a str>, repeater: Option<Cow<'a, str>>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
delay: Option<&'a str>, delay: Option<Cow<'a, str>>,
}, },
Diary { Diary {
value: &'a str, value: Cow<'a, str>,
}, },
} }
@ -258,7 +260,12 @@ impl Timestamp<'_> {
let (input, value) = take_till(|c| c == ')' || c == '>' || c == '\n')(input)?; let (input, value) = take_till(|c| c == ')' || c == '>' || c == '\n')(input)?;
let (input, _) = tag(")>")(input)?; let (input, _) = tag(")>")(input)?;
Ok((input, Timestamp::Diary { value })) Ok((
input,
Timestamp::Diary {
value: value.into(),
},
))
} }
} }
@ -320,7 +327,7 @@ fn parse() {
year: 2003, year: 2003,
month: 9, month: 9,
day: 16, day: 16,
dayname: "Tue", dayname: "Tue".into(),
hour: None, hour: None,
minute: None minute: None
}, },
@ -338,7 +345,7 @@ fn parse() {
year: 2003, year: 2003,
month: 9, month: 9,
day: 16, day: 16,
dayname: "Tue", dayname: "Tue".into(),
hour: Some(9), hour: Some(9),
minute: Some(39) minute: Some(39)
}, },
@ -346,7 +353,7 @@ fn parse() {
year: 2003, year: 2003,
month: 9, month: 9,
day: 16, day: 16,
dayname: "Tue", dayname: "Tue".into(),
hour: Some(10), hour: Some(10),
minute: Some(39), minute: Some(39),
}, },
@ -364,7 +371,7 @@ fn parse() {
year: 2003, year: 2003,
month: 9, month: 9,
day: 16, day: 16,
dayname: "Tue", dayname: "Tue".into(),
hour: Some(9), hour: Some(9),
minute: Some(39), minute: Some(39),
}, },
@ -372,7 +379,7 @@ fn parse() {
year: 2003, year: 2003,
month: 9, month: 9,
day: 16, day: 16,
dayname: "Tue", dayname: "Tue".into(),
hour: Some(10), hour: Some(10),
minute: Some(39), minute: Some(39),
}, },

View file

@ -1,14 +1,17 @@
//! Headline Title //! Headline Title
use std::borrow::Cow;
use memchr::memrchr; use memchr::memrchr;
use nom::{ use nom::{
bytes::complete::{tag, take_until, take_while}, bytes::complete::{tag, take_until, take_while},
character::complete::{anychar, space1}, character::complete::{anychar, space1},
combinator::{map, map_parser, opt, verify}, combinator::{map, map_parser, opt, verify},
error::ErrorKind,
error_position,
multi::fold_many0, multi::fold_many0,
sequence::delimited, sequence::{delimited, preceded},
sequence::preceded, Err, IResult,
IResult,
}; };
use std::collections::HashMap; use std::collections::HashMap;
@ -27,20 +30,23 @@ pub struct Title<'a> {
pub priority: Option<char>, pub priority: Option<char>,
/// headline tags, including the sparated colons /// headline tags, including the sparated colons
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Vec::is_empty"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Vec::is_empty"))]
pub tags: Vec<&'a str>, pub tags: Vec<Cow<'a, str>>,
/// headline keyword /// headline keyword
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub keyword: Option<&'a str>, pub keyword: Option<Cow<'a, str>>,
pub raw: &'a str, pub raw: Cow<'a, str>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
pub planning: Option<Box<Planning<'a>>>, pub planning: Option<Box<Planning<'a>>>,
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "HashMap::is_empty"))] #[cfg_attr(feature = "ser", serde(skip_serializing_if = "HashMap::is_empty"))]
pub properties: HashMap<&'a str, &'a str>, pub properties: HashMap<Cow<'a, str>, Cow<'a, str>>,
} }
impl Title<'_> { impl Title<'_> {
#[inline] #[inline]
pub(crate) fn parse<'a>(input: &'a str, config: &ParseConfig) -> IResult<&'a str, Title<'a>> { pub(crate) fn parse<'a>(
input: &'a str,
config: &ParseConfig,
) -> IResult<&'a str, (Title<'a>, &'a str)> {
let (input, (level, keyword, priority, raw, tags)) = parse_headline(input, config)?; let (input, (level, keyword, priority, raw, tags)) = parse_headline(input, config)?;
let (input, planning) = Planning::parse(input) let (input, planning) = Planning::parse(input)
@ -51,15 +57,18 @@ impl Title<'_> {
Ok(( Ok((
input, input,
(
Title { Title {
properties: properties.unwrap_or_default(), properties: properties.unwrap_or_default(),
level, level,
keyword, keyword: keyword.map(Into::into),
priority, priority,
tags, tags,
raw, raw: raw.into(),
planning, planning,
}, },
raw,
),
)) ))
} }
} }
@ -67,7 +76,16 @@ impl Title<'_> {
fn parse_headline<'a>( fn parse_headline<'a>(
input: &'a str, input: &'a str,
config: &ParseConfig, config: &ParseConfig,
) -> IResult<&'a str, (usize, Option<&'a str>, Option<char>, &'a str, Vec<&'a str>)> { ) -> IResult<
&'a str,
(
usize,
Option<&'a str>,
Option<char>,
&'a str,
Vec<Cow<'a, str>>,
),
> {
let (input, level) = map(take_while(|c: char| c == '*'), |s: &str| s.len())(input)?; let (input, level) = map(take_while(|c: char| c == '*'), |s: &str| s.len())(input)?;
debug_assert!(level > 0); debug_assert!(level > 0);
@ -103,19 +121,24 @@ fn parse_headline<'a>(
keyword, keyword,
priority, priority,
raw, raw,
tags.split(':').filter(|s| !s.is_empty()).collect(), tags.split(':')
.filter(|s| !s.is_empty())
.map(Into::into)
.collect(),
), ),
)) ))
} }
fn parse_properties_drawer(input: &str) -> IResult<&str, HashMap<&str, &str>> { fn parse_properties_drawer(input: &str) -> IResult<&str, HashMap<Cow<'_, str>, Cow<'_, str>>> {
let (input, (drawer, content)) = Drawer::parse(input)?; let (input, (drawer, content)) = Drawer::parse(input)?;
let _ = tag("PROPERTIES")(drawer.name)?; if drawer.name != "PROPERTIES" {
return Err(Err::Error(error_position!(input, ErrorKind::Tag)));
}
let (_, map) = fold_many0( let (_, map) = fold_many0(
parse_node_property, parse_node_property,
HashMap::new(), HashMap::new(),
|mut acc: HashMap<_, _>, (name, value)| { |mut acc: HashMap<_, _>, (name, value)| {
acc.insert(name, value); acc.insert(name.into(), value.into());
acc acc
}, },
)(content)?; )(content)?;
@ -134,7 +157,7 @@ fn parse_node_property(input: &str) -> IResult<&str, (&str, &str)> {
impl Title<'_> { impl Title<'_> {
/// checks if this headline is "archived" /// checks if this headline is "archived"
pub fn is_archived(&self) -> bool { pub fn is_archived(&self) -> bool {
self.tags.contains(&"ARCHIVE") self.tags.iter().any(|tag| tag == "ARCHIVE")
} }
} }
@ -154,7 +177,7 @@ fn parse_headline_() {
Some("DONE"), Some("DONE"),
Some('A'), Some('A'),
"COMMENT Title", "COMMENT Title",
vec!["tag", "a2%"] vec!["tag".into(), "a2%".into()]
) )
)) ))
); );

View file

@ -3,14 +3,14 @@ use jetscii::bytes;
use std::fmt; use std::fmt;
use std::io::{Error, Write}; use std::io::{Error, Write};
pub struct Escape<'a>(pub &'a str); pub struct Escape<S: AsRef<str>>(pub S);
impl fmt::Display for Escape<'_> { impl<S: AsRef<str>> fmt::Display for Escape<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut pos = 0; let mut pos = 0;
let bytes = self.0.as_bytes(); let bytes = self.0.as_ref().as_bytes();
while let Some(off) = bytes!(b'<', b'>', b'&', b'\'', b'"').find(&bytes[pos..]) { while let Some(off) = bytes!(b'<', b'>', b'&', b'\'', b'"').find(&bytes[pos..]) {
write!(f, "{}", &self.0[pos..pos + off])?; write!(f, "{}", &self.0.as_ref()[pos..pos + off])?;
pos += off + 1; pos += off + 1;
@ -24,7 +24,7 @@ impl fmt::Display for Escape<'_> {
} }
} }
write!(f, "{}", &self.0[pos..]) write!(f, "{}", &self.0.as_ref()[pos..])
} }
} }
@ -57,9 +57,11 @@ pub trait HtmlHandler<E: From<Error>> {
Underline => write!(w, "<u>")?, Underline => write!(w, "<u>")?,
// non-container elements // non-container elements
CommentBlock(_) => (), CommentBlock(_) => (),
ExampleBlock(block) => { ExampleBlock(block) => write!(
write!(w, "<pre class=\"example\">{}</pre>", Escape(block.contents))? w,
} "<pre class=\"example\">{}</pre>",
Escape(&block.contents)
)?,
ExportBlock(block) => { ExportBlock(block) => {
if block.data.eq_ignore_ascii_case("HTML") { if block.data.eq_ignore_ascii_case("HTML") {
write!(w, "{}", block.contents)? write!(w, "{}", block.contents)?
@ -67,13 +69,17 @@ pub trait HtmlHandler<E: From<Error>> {
} }
SourceBlock(block) => { SourceBlock(block) => {
if block.language.is_empty() { if block.language.is_empty() {
write!(w, "<pre class=\"example\">{}</pre>", Escape(block.contents))?; write!(
w,
"<pre class=\"example\">{}</pre>",
Escape(&block.contents)
)?;
} else { } else {
write!( write!(
w, w,
"<div class=\"org-src-container\"><pre class=\"src src-{}\">{}</pre></div>", "<div class=\"org-src-container\"><pre class=\"src src-{}\">{}</pre></div>",
block.language, block.language,
Escape(block.contents) Escape(&block.contents)
)?; )?;
} }
} }
@ -82,7 +88,7 @@ pub trait HtmlHandler<E: From<Error>> {
w, w,
"<code class=\"src src-{}\">{}</code>", "<code class=\"src src-{}\">{}</code>",
inline_src.lang, inline_src.lang,
Escape(inline_src.body) Escape(&inline_src.body)
)?, )?,
Code { value } => write!(w, "<code>{}</code>", Escape(value))?, Code { value } => write!(w, "<code>{}</code>", Escape(value))?,
FnRef(_fn_ref) => (), FnRef(_fn_ref) => (),
@ -90,8 +96,8 @@ pub trait HtmlHandler<E: From<Error>> {
Link(link) => write!( Link(link) => write!(
w, w,
"<a href=\"{}\">{}</a>", "<a href=\"{}\">{}</a>",
Escape(link.path), Escape(&link.path),
Escape(link.desc.unwrap_or(link.path)), Escape(link.desc.as_ref().unwrap_or(&link.path)),
)?, )?,
Macros(_macros) => (), Macros(_macros) => (),
RadioTarget(_radio_target) => (), RadioTarget(_radio_target) => (),

View file

@ -15,7 +15,7 @@ pub trait OrgHandler<E: From<Error>> {
Document => (), Document => (),
DynBlock(dyn_block) => { DynBlock(dyn_block) => {
write!(&mut w, "#+BEGIN: {}", dyn_block.block_name)?; write!(&mut w, "#+BEGIN: {}", dyn_block.block_name)?;
if let Some(parameters) = dyn_block.arguments { if let Some(parameters) = &dyn_block.arguments {
write!(&mut w, " {}", parameters)?; write!(&mut w, " {}", parameters)?;
} }
writeln!(&mut w)?; writeln!(&mut w)?;
@ -49,7 +49,7 @@ pub trait OrgHandler<E: From<Error>> {
BabelCall(_babel_call) => (), BabelCall(_babel_call) => (),
InlineSrc(inline_src) => { InlineSrc(inline_src) => {
write!(&mut w, "src_{}", inline_src.lang)?; write!(&mut w, "src_{}", inline_src.lang)?;
if let Some(options) = inline_src.options { if let Some(options) = &inline_src.options {
write!(&mut w, "[{}]", options)?; write!(&mut w, "[{}]", options)?;
} }
write!(&mut w, "{{{}}}", inline_src.body)?; write!(&mut w, "{{{}}}", inline_src.body)?;
@ -57,24 +57,24 @@ pub trait OrgHandler<E: From<Error>> {
Code { value } => write!(w, "~{}~", value)?, Code { value } => write!(w, "~{}~", value)?,
FnRef(fn_ref) => { FnRef(fn_ref) => {
write!(&mut w, "[fn:{}", fn_ref.label)?; write!(&mut w, "[fn:{}", fn_ref.label)?;
if let Some(definition) = fn_ref.definition { if let Some(definition) = &fn_ref.definition {
write!(&mut w, ":{}", definition)?; write!(&mut w, ":{}", definition)?;
} }
write!(&mut w, "]")?; write!(&mut w, "]")?;
} }
InlineCall(inline_call) => { InlineCall(inline_call) => {
write!(&mut w, "call_{}", inline_call.name)?; write!(&mut w, "call_{}", inline_call.name)?;
if let Some(header) = inline_call.inside_header { if let Some(header) = &inline_call.inside_header {
write!(&mut w, "[{}]", header)?; write!(&mut w, "[{}]", header)?;
} }
write!(&mut w, "({})", inline_call.arguments)?; write!(&mut w, "({})", inline_call.arguments)?;
if let Some(header) = inline_call.end_header { if let Some(header) = &inline_call.end_header {
write!(&mut w, "[{}]", header)?; write!(&mut w, "[{}]", header)?;
} }
} }
Link(link) => { Link(link) => {
write!(&mut w, "[[{}]", link.path)?; write!(&mut w, "[[{}]", link.path)?;
if let Some(desc) = link.desc { if let Some(desc) = &link.desc {
write!(&mut w, "[{}]", desc)?; write!(&mut w, "[{}]", desc)?;
} }
write!(&mut w, "]")?; write!(&mut w, "]")?;
@ -130,7 +130,7 @@ pub trait OrgHandler<E: From<Error>> {
FixedWidth { value } => write!(w, "{}", value)?, FixedWidth { value } => write!(w, "{}", value)?,
Keyword(keyword) => { Keyword(keyword) => {
write!(&mut w, "#+{}", keyword.key)?; write!(&mut w, "#+{}", keyword.key)?;
if let Some(optional) = keyword.optional { if let Some(optional) = &keyword.optional {
write!(&mut w, "[{}]", optional)?; write!(&mut w, "[{}]", optional)?;
} }
writeln!(&mut w, ": {}", keyword.value)?; writeln!(&mut w, ": {}", keyword.value)?;
@ -141,7 +141,7 @@ pub trait OrgHandler<E: From<Error>> {
for _ in 0..title.level { for _ in 0..title.level {
write!(&mut w, "*")?; write!(&mut w, "*")?;
} }
if let Some(keyword) = title.keyword { if let Some(keyword) = &title.keyword {
write!(&mut w, " {}", keyword)?; write!(&mut w, " {}", keyword)?;
} }
if let Some(priority) = title.priority { if let Some(priority) = title.priority {

View file

@ -125,7 +125,7 @@
//! w, //! w,
//! "<h{0}><a id=\"{1}\" href=\"#{1}\">", //! "<h{0}><a id=\"{1}\" href=\"#{1}\">",
//! title.level, //! title.level,
//! slugify!(title.raw), //! slugify!(&title.raw),
//! )?; //! )?;
//! } //! }
//! } //! }

View file

@ -10,6 +10,7 @@ use nom::{
error::ErrorKind, error::ErrorKind,
error_position, Err, IResult, error_position, Err, IResult,
}; };
use std::borrow::Cow;
use crate::config::ParseConfig; use crate::config::ParseConfig;
use crate::elements::*; use crate::elements::*;
@ -50,8 +51,7 @@ pub fn parse_title<'a>(
containers: &mut Vec<Container<'a>>, containers: &mut Vec<Container<'a>>,
config: &ParseConfig, config: &ParseConfig,
) -> &'a str { ) -> &'a str {
let (tail, title) = Title::parse(content, config).unwrap(); let (tail, (title, content)) = Title::parse(content, config).unwrap();
let content = title.raw;
let node = arena.new_node(Element::Title(title)); let node = arena.new_node(Element::Title(title));
parent.append(node, arena); parent.append(node, arena);
containers.push(Container::Inline { content, node }); containers.push(Container::Inline { content, node });
@ -294,33 +294,42 @@ pub fn parse_block<'a>(
"COMMENT" => { "COMMENT" => {
let node = arena.new_node(Element::CommentBlock(CommentBlock { let node = arena.new_node(Element::CommentBlock(CommentBlock {
data: block.args, data: block.args,
contents: content, contents: content.into(),
})); }));
Some((tail, node)) Some((tail, node))
} }
"EXAMPLE" => { "EXAMPLE" => {
let node = arena.new_node(Element::ExampleBlock(ExampleBlock { let node = arena.new_node(Element::ExampleBlock(ExampleBlock {
data: block.args, data: block.args,
contents: content, contents: content.into(),
})); }));
Some((tail, node)) Some((tail, node))
} }
"EXPORT" => { "EXPORT" => {
let node = arena.new_node(Element::ExportBlock(ExportBlock { let node = arena.new_node(Element::ExportBlock(ExportBlock {
data: block.args.unwrap_or(""), data: block.args.unwrap_or_default(),
contents: content, contents: content.into(),
})); }));
Some((tail, node)) Some((tail, node))
} }
"SRC" => { "SRC" => {
let (language, arguments) = block let (language, arguments) = match &block.args {
.args Some(Cow::Borrowed(args)) => {
.map(|args| args.split_at(args.find(' ').unwrap_or_else(|| args.len()))) let (language, arguments) =
.unwrap_or(("", "")); args.split_at(args.find(' ').unwrap_or_else(|| args.len()));
(Cow::Borrowed(language), Cow::Borrowed(arguments))
}
Some(Cow::Owned(args)) => {
let (language, arguments) =
args.split_at(args.find(' ').unwrap_or_else(|| args.len()));
(Cow::Owned(language.into()), Cow::Owned(arguments.into()))
}
None => (Cow::Borrowed(""), Cow::Borrowed("")),
};
let node = arena.new_node(Element::SourceBlock(SourceBlock { let node = arena.new_node(Element::SourceBlock(SourceBlock {
arguments, arguments,
language, language,
contents: content, contents: content.into(),
})); }));
Some((tail, node)) Some((tail, node))
} }
@ -605,7 +614,7 @@ pub fn prase_table<'a>(
Some(( Some((
&contents[last_end..], &contents[last_end..],
arena.new_node(Element::Table(Table::TableEl { arena.new_node(Element::Table(Table::TableEl {
value: &contents[0..last_end], value: contents[0..last_end].into(),
})), })),
)) ))
}; };
@ -615,7 +624,9 @@ pub fn prase_table<'a>(
Some(( Some((
"", "",
arena.new_node(Element::Table(Table::TableEl { value: contents })), arena.new_node(Element::Table(Table::TableEl {
value: contents.into(),
})),
)) ))
} else { } else {
None None