diff --git a/examples/custom.rs b/examples/custom.rs index 7b41e97..a708afb 100644 --- a/examples/custom.rs +++ b/examples/custom.rs @@ -42,7 +42,7 @@ impl HtmlHandler for MyHtmlHandler { w, "", title.level, - slugify!(title.raw), + slugify!(&title.raw), )?; } } diff --git a/examples/syntect.rs b/examples/syntect.rs index 9e61772..b9eda56 100644 --- a/examples/syntect.rs +++ b/examples/syntect.rs @@ -46,7 +46,7 @@ impl HtmlHandler for SyntectHtmlHandler { Element::InlineSrc(inline_src) => write!( w, "{}", - 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 for SyntectHtmlHandler { w, "
{}
", block.language, - self.highlight(Some(block.language), block.contents) + self.highlight(Some(&block.language), &block.contents) )? } } @@ -68,7 +68,7 @@ impl HtmlHandler for SyntectHtmlHandler { Element::ExampleBlock(block) => write!( w, "
{}
", - self.highlight(None, block.contents) + self.highlight(None, &block.contents) )?, _ => self.default_hanlder.start(w, element)?, } diff --git a/src/elements/block.rs b/src/elements/block.rs index 8bcec09..83bb027 100644 --- a/src/elements/block.rs +++ b/src/elements/block.rs @@ -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>, } 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>, + 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>, } #[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>, } #[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>, } #[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>, + 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>, + 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>, } diff --git a/src/elements/clock.rs b/src/elements/clock.rs index 7fbbaab..c764823 100644 --- a/src/elements/clock.rs +++ b/src/elements/clock.rs @@ -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>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - delay: Option<&'a str>, - duration: &'a str, + delay: Option>, + 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>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - delay: Option<&'a str>, + delay: Option>, }, } @@ -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(), }) )) ); diff --git a/src/elements/cookie.rs b/src/elements/cookie.rs index f02607b..7dcea8a 100644 --- a/src/elements/cookie.rs +++ b/src/elements/cookie.rs @@ -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()); diff --git a/src/elements/drawer.rs b/src/elements/drawer.rs index cc764e3..516107d 100644 --- a/src/elements/drawer.rs +++ b/src/elements/drawer.rs @@ -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" + ) + )) ) } diff --git a/src/elements/dyn_block.rs b/src/elements/dyn_block.rs index 65c2744..9da0519 100644 --- a/src/elements/dyn_block.rs +++ b/src/elements/dyn_block.rs @@ -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>, } 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" ) diff --git a/src/elements/fn_def.rs b/src/elements/fn_def.rs index a5e03c3..ca4efbe 100644 --- a/src/elements/fn_def.rs +++ b/src/elements/fn_def.rs @@ -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); diff --git a/src/elements/fn_ref.rs b/src/elements/fn_ref.rs index eb6e2db..e1c0d98 100644 --- a/src/elements/fn_ref.rs +++ b/src/elements/fn_ref.rs @@ -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>, } 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()) }, )) ); diff --git a/src/elements/inline_call.rs b/src/elements/inline_call.rs index fc51ee6..9b2edf9 100644 --- a/src/elements/inline_call.rs +++ b/src/elements/inline_call.rs @@ -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>, + 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>, } 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()), }), )) ); diff --git a/src/elements/inline_src.rs b/src/elements/inline_src.rs index 046df20..ebac5ab 100644 --- a/src/elements/inline_src.rs +++ b/src/elements/inline_src.rs @@ -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>, + 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: "text", + lang: "xml".into(), + options: Some(":exports code".into()), + body: "text".into(), }), )) ); diff --git a/src/elements/keyword.rs b/src/elements/keyword.rs index 810d603..747f0fd 100644 --- a/src/elements/keyword.rs +++ b/src/elements/keyword.rs @@ -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>, + 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(), }) )) ); diff --git a/src/elements/link.rs b/src/elements/link.rs index d202ed6..15e7af1 100644 --- a/src/elements/link.rs +++ b/src/elements/link.rs @@ -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>, } 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()) }) )) ); diff --git a/src/elements/list.rs b/src/elements/list.rs index d945656..cb20541 100644 --- a/src/elements/list.rs +++ b/src/elements/list.rs @@ -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..], ) diff --git a/src/elements/macros.rs b/src/elements/macros.rs index 7830ce6..5caab9f 100644 --- a/src/elements/macros.rs +++ b/src/elements/macros.rs @@ -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>, } 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 }) )) diff --git a/src/elements/planning.rs b/src/elements/planning.rs index 340ad42..95603f1 100644 --- a/src/elements/planning.rs +++ b/src/elements/planning.rs @@ -80,7 +80,7 @@ fn prase() { year: 2019, month: 4, day: 8, - dayname: "Mon", + dayname: "Mon".into(), hour: None, minute: None }, diff --git a/src/elements/snippet.rs b/src/elements/snippet.rs index 3708fcc..a2baaf9 100644 --- a/src/elements/snippet.rs +++ b/src/elements/snippet.rs @@ -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: "" + name: "html".into(), + value: "".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: "

@

", + name: "html".into(), + value: "

@

".into(), }) )) ); diff --git a/src/elements/table.rs b/src/elements/table.rs index 815100f..4d58c72 100644 --- a/src/elements/table.rs +++ b/src/elements/table.rs @@ -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> }, #[cfg_attr(feature = "ser", serde(rename = "table.el"))] - TableEl { value: &'a str }, + TableEl { value: Cow<'a, str> }, } #[derive(Debug)] diff --git a/src/elements/target.rs b/src/elements/target.rs index 7eb9d9e..49d4634 100644 --- a/src/elements/target.rs +++ b/src/elements/target.rs @@ -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("<>"), - Ok(("", Element::Target(Target { target: "target" }))) + Ok(( + "", + Element::Target(Target { + target: "target".into() + }) + )) ); assert_eq!( Target::parse("<>"), - Ok(("", Element::Target(Target { target: "tar get" }))) + Ok(( + "", + Element::Target(Target { + target: "tar get".into() + }) + )) ); assert!(Target::parse("<>").is_err()); assert!(Target::parse("<< target>>").is_err()); diff --git a/src/elements/timestamp.rs b/src/elements/timestamp.rs index 345f7a4..e4a1dea 100644 --- a/src/elements/timestamp.rs +++ b/src/elements/timestamp.rs @@ -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, #[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>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - delay: Option<&'a str>, + delay: Option>, }, Inactive { start: Datetime<'a>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - repeater: Option<&'a str>, + repeater: Option>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - delay: Option<&'a str>, + delay: Option>, }, ActiveRange { start: Datetime<'a>, end: Datetime<'a>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - repeater: Option<&'a str>, + repeater: Option>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - delay: Option<&'a str>, + delay: Option>, }, InactiveRange { start: Datetime<'a>, end: Datetime<'a>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - repeater: Option<&'a str>, + repeater: Option>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] - delay: Option<&'a str>, + delay: Option>, }, 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), }, diff --git a/src/elements/title.rs b/src/elements/title.rs index 814dcb3..5e0a72e 100644 --- a/src/elements/title.rs +++ b/src/elements/title.rs @@ -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, /// 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>, /// 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>, + pub raw: Cow<'a, str>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))] pub planning: Option>>, #[cfg_attr(feature = "ser", serde(skip_serializing_if = "HashMap::is_empty"))] - pub properties: HashMap<&'a str, &'a str>, + pub properties: HashMap, 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, &'a str, Vec<&'a str>)> { +) -> IResult< + &'a str, + ( + usize, + Option<&'a str>, + Option, + &'a str, + Vec>, + ), +> { 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>>> { 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()] ) )) ); diff --git a/src/export/html.rs b/src/export/html.rs index a07a126..9f4d25a 100644 --- a/src/export/html.rs +++ b/src/export/html.rs @@ -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>(pub S); -impl fmt::Display for Escape<'_> { +impl> fmt::Display for Escape { 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> { Underline => write!(w, "")?, // non-container elements CommentBlock(_) => (), - ExampleBlock(block) => { - write!(w, "
{}
", Escape(block.contents))? - } + ExampleBlock(block) => write!( + w, + "
{}
", + Escape(&block.contents) + )?, ExportBlock(block) => { if block.data.eq_ignore_ascii_case("HTML") { write!(w, "{}", block.contents)? @@ -67,13 +69,17 @@ pub trait HtmlHandler> { } SourceBlock(block) => { if block.language.is_empty() { - write!(w, "
{}
", Escape(block.contents))?; + write!( + w, + "
{}
", + Escape(&block.contents) + )?; } else { write!( w, "
{}
", block.language, - Escape(block.contents) + Escape(&block.contents) )?; } } @@ -82,7 +88,7 @@ pub trait HtmlHandler> { w, "{}", inline_src.lang, - Escape(inline_src.body) + Escape(&inline_src.body) )?, Code { value } => write!(w, "{}", Escape(value))?, FnRef(_fn_ref) => (), @@ -90,8 +96,8 @@ pub trait HtmlHandler> { Link(link) => write!( w, "
{}", - 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) => (), diff --git a/src/export/org.rs b/src/export/org.rs index fff11fe..2bbad38 100644 --- a/src/export/org.rs +++ b/src/export/org.rs @@ -15,7 +15,7 @@ pub trait OrgHandler> { 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> { 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> { 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> { 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> { 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 { diff --git a/src/lib.rs b/src/lib.rs index 0c2773d..c66b6dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,7 +125,7 @@ //! w, //! "", //! title.level, -//! slugify!(title.raw), +//! slugify!(&title.raw), //! )?; //! } //! } diff --git a/src/parsers.rs b/src/parsers.rs index cc16b7b..a91ac33 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -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>, 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