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,
"<h{0}><a id=\"{1}\" href=\"#{1}\">",
title.level,
slugify!(title.raw),
slugify!(&title.raw),
)?;
}
}

View file

@ -46,7 +46,7 @@ impl HtmlHandler<Error> for SyntectHtmlHandler {
Element::InlineSrc(inline_src) => write!(
w,
"<code>{}</code>",
self.highlight(Some(inline_src.lang), inline_src.body)
self.highlight(Some(&inline_src.lang), &inline_src.body)
)?,
Element::SourceBlock(block) => {
if block.language.is_empty() {
@ -56,7 +56,7 @@ impl HtmlHandler<Error> for SyntectHtmlHandler {
w,
"<div class=\"org-src-container\"><pre class=\"src src-{}\">{}</pre></div>",
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!(
w,
"<pre class=\"example\">{}</pre>",
self.highlight(None, block.contents)
self.highlight(None, &block.contents)
)?,
_ => 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 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))]
#[derive(Debug)]
pub struct Block<'a> {
pub name: &'a str,
pub args: Option<&'a str>,
pub name: Cow<'a, str>,
pub args: Option<Cow<'a, str>>,
}
impl Block<'_> {
@ -22,8 +24,12 @@ impl Block<'_> {
input,
(
Block {
name,
args: if args.is_empty() { None } else { Some(args) },
name: name.into(),
args: if args.is_empty() {
None
} else {
Some(args.into())
},
},
contents,
),
@ -39,7 +45,7 @@ fn parse() {
"",
(
Block {
name: "SRC",
name: "SRC".into(),
args: None,
},
""
@ -52,8 +58,8 @@ fn parse() {
"",
(
Block {
name: "SRC",
args: Some("javascript"),
name: "SRC".into(),
args: Some("javascript".into()),
},
"console.log('Hello World!');\n"
)
@ -66,60 +72,60 @@ fn parse() {
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct SpecialBlock<'a> {
pub parameters: Option<&'a str>,
pub name: &'a str,
pub parameters: Option<Cow<'a, str>>,
pub name: Cow<'a, str>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct QuoteBlock<'a> {
pub parameters: Option<&'a str>,
pub parameters: Option<Cow<'a, str>>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct CenterBlock<'a> {
pub parameters: Option<&'a str>,
pub parameters: Option<Cow<'a, str>>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct VerseBlock<'a> {
pub parameters: Option<&'a str>,
pub parameters: Option<Cow<'a, str>>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct CommentBlock<'a> {
pub data: Option<&'a str>,
pub contents: &'a str,
pub data: Option<Cow<'a, str>>,
pub contents: Cow<'a, str>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct ExampleBlock<'a> {
pub data: Option<&'a str>,
pub contents: &'a str,
pub data: Option<Cow<'a, str>>,
pub contents: Cow<'a, str>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct ExportBlock<'a> {
pub data: &'a str,
pub contents: &'a str,
pub data: Cow<'a, str>,
pub contents: Cow<'a, str>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
pub struct SourceBlock<'a> {
pub contents: &'a str,
pub language: &'a str,
pub arguments: &'a str,
pub contents: Cow<'a, str>,
pub language: Cow<'a, str>,
pub arguments: Cow<'a, str>,
}

View file

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

View file

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

View file

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

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use memchr::memchr;
use nom::{
bytes::complete::{tag, take_while1},
@ -9,7 +11,7 @@ use nom::{
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[derive(Debug)]
pub struct FnDef<'a> {
pub label: &'a str,
pub label: Cow<'a, 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());
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() {
assert_eq!(
FnDef::parse("[fn:1] https://orgmode.org"),
Some(("", FnDef { label: "1" }, " https://orgmode.org"))
Some(("", FnDef { label: "1".into() }, " https://orgmode.org"))
);
assert_eq!(
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!(
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!(
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:wor d] https://orgmode.org"), None);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,14 @@
use std::borrow::Cow;
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
#[cfg_attr(feature = "ser", serde(tag = "table_type"))]
pub enum Table<'a> {
#[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"))]
TableEl { value: &'a str },
TableEl { value: Cow<'a, str> },
}
#[derive(Debug)]

View file

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

View file

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

View file

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

View file

@ -3,14 +3,14 @@ use jetscii::bytes;
use std::fmt;
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 {
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..]) {
write!(f, "{}", &self.0[pos..pos + off])?;
write!(f, "{}", &self.0.as_ref()[pos..pos + off])?;
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>")?,
// non-container elements
CommentBlock(_) => (),
ExampleBlock(block) => {
write!(w, "<pre class=\"example\">{}</pre>", Escape(block.contents))?
}
ExampleBlock(block) => write!(
w,
"<pre class=\"example\">{}</pre>",
Escape(&block.contents)
)?,
ExportBlock(block) => {
if block.data.eq_ignore_ascii_case("HTML") {
write!(w, "{}", block.contents)?
@ -67,13 +69,17 @@ pub trait HtmlHandler<E: From<Error>> {
}
SourceBlock(block) => {
if block.language.is_empty() {
write!(w, "<pre class=\"example\">{}</pre>", Escape(block.contents))?;
write!(
w,
"<pre class=\"example\">{}</pre>",
Escape(&block.contents)
)?;
} else {
write!(
w,
"<div class=\"org-src-container\"><pre class=\"src src-{}\">{}</pre></div>",
block.language,
Escape(block.contents)
Escape(&block.contents)
)?;
}
}
@ -82,7 +88,7 @@ pub trait HtmlHandler<E: From<Error>> {
w,
"<code class=\"src src-{}\">{}</code>",
inline_src.lang,
Escape(inline_src.body)
Escape(&inline_src.body)
)?,
Code { value } => write!(w, "<code>{}</code>", Escape(value))?,
FnRef(_fn_ref) => (),
@ -90,8 +96,8 @@ pub trait HtmlHandler<E: From<Error>> {
Link(link) => write!(
w,
"<a href=\"{}\">{}</a>",
Escape(link.path),
Escape(link.desc.unwrap_or(link.path)),
Escape(&link.path),
Escape(link.desc.as_ref().unwrap_or(&link.path)),
)?,
Macros(_macros) => (),
RadioTarget(_radio_target) => (),

View file

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

View file

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

View file

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