feat(elements): into_owned
function
This commit is contained in:
parent
9bc260627b
commit
f2d0a1dd2d
|
@ -12,6 +12,15 @@ pub struct SpecialBlock<'a> {
|
||||||
pub name: Cow<'a, str>,
|
pub name: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SpecialBlock<'_> {
|
||||||
|
pub fn into_owned(self) -> SpecialBlock<'static> {
|
||||||
|
SpecialBlock {
|
||||||
|
name: self.name.into_owned().into(),
|
||||||
|
parameters: self.parameters.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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))]
|
||||||
|
@ -19,6 +28,14 @@ pub struct QuoteBlock<'a> {
|
||||||
pub parameters: Option<Cow<'a, str>>,
|
pub parameters: Option<Cow<'a, str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl QuoteBlock<'_> {
|
||||||
|
pub fn into_owned(self) -> QuoteBlock<'static> {
|
||||||
|
QuoteBlock {
|
||||||
|
parameters: self.parameters.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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))]
|
||||||
|
@ -26,6 +43,14 @@ pub struct CenterBlock<'a> {
|
||||||
pub parameters: Option<Cow<'a, str>>,
|
pub parameters: Option<Cow<'a, str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CenterBlock<'_> {
|
||||||
|
pub fn into_owned(self) -> CenterBlock<'static> {
|
||||||
|
CenterBlock {
|
||||||
|
parameters: self.parameters.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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))]
|
||||||
|
@ -33,6 +58,14 @@ pub struct VerseBlock<'a> {
|
||||||
pub parameters: Option<Cow<'a, str>>,
|
pub parameters: Option<Cow<'a, str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl VerseBlock<'_> {
|
||||||
|
pub fn into_owned(self) -> VerseBlock<'static> {
|
||||||
|
VerseBlock {
|
||||||
|
parameters: self.parameters.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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))]
|
||||||
|
@ -41,6 +74,15 @@ pub struct CommentBlock<'a> {
|
||||||
pub contents: Cow<'a, str>,
|
pub contents: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CommentBlock<'_> {
|
||||||
|
pub fn into_owned(self) -> CommentBlock<'static> {
|
||||||
|
CommentBlock {
|
||||||
|
data: self.data.map(Into::into).map(Cow::Owned),
|
||||||
|
contents: self.contents.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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))]
|
||||||
|
@ -49,6 +91,15 @@ pub struct ExampleBlock<'a> {
|
||||||
pub contents: Cow<'a, str>,
|
pub contents: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExampleBlock<'_> {
|
||||||
|
pub fn into_owned(self) -> ExampleBlock<'static> {
|
||||||
|
ExampleBlock {
|
||||||
|
data: self.data.map(Into::into).map(Cow::Owned),
|
||||||
|
contents: self.contents.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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))]
|
||||||
|
@ -57,6 +108,15 @@ pub struct ExportBlock<'a> {
|
||||||
pub contents: Cow<'a, str>,
|
pub contents: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExportBlock<'_> {
|
||||||
|
pub fn into_owned(self) -> ExportBlock<'static> {
|
||||||
|
ExportBlock {
|
||||||
|
data: self.data.into_owned().into(),
|
||||||
|
contents: self.contents.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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))]
|
||||||
|
@ -66,6 +126,16 @@ pub struct SourceBlock<'a> {
|
||||||
pub arguments: Cow<'a, str>,
|
pub arguments: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SourceBlock<'_> {
|
||||||
|
pub fn into_owned(self) -> SourceBlock<'static> {
|
||||||
|
SourceBlock {
|
||||||
|
language: self.language.into_owned().into(),
|
||||||
|
arguments: self.arguments.into_owned().into(),
|
||||||
|
contents: self.contents.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_block_element(input: &str) -> IResult<&str, (&str, Option<&str>, &str)> {
|
pub(crate) fn parse_block_element(input: &str) -> IResult<&str, (&str, Option<&str>, &str)> {
|
||||||
let (input, name) = preceded(tag_no_case("#+BEGIN_"), alpha1)(input)?;
|
let (input, name) = preceded(tag_no_case("#+BEGIN_"), alpha1)(input)?;
|
||||||
let (input, args) = line(input)?;
|
let (input, args) = line(input)?;
|
||||||
|
|
|
@ -90,6 +90,33 @@ impl Clock<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_onwed(self) -> Clock<'static> {
|
||||||
|
match self {
|
||||||
|
Clock::Closed {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
repeater,
|
||||||
|
delay,
|
||||||
|
duration,
|
||||||
|
} => Clock::Closed {
|
||||||
|
start: start.into_owned(),
|
||||||
|
end: end.into_owned(),
|
||||||
|
repeater: repeater.map(Into::into).map(Cow::Owned),
|
||||||
|
delay: delay.map(Into::into).map(Cow::Owned),
|
||||||
|
duration: duration.into_owned().into(),
|
||||||
|
},
|
||||||
|
Clock::Running {
|
||||||
|
start,
|
||||||
|
repeater,
|
||||||
|
delay,
|
||||||
|
} => Clock::Running {
|
||||||
|
start: start.into_owned(),
|
||||||
|
repeater: repeater.map(Into::into).map(Cow::Owned),
|
||||||
|
delay: delay.map(Into::into).map(Cow::Owned),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// returns `true` if the clock is running
|
/// returns `true` if the clock is running
|
||||||
pub fn is_running(&self) -> bool {
|
pub fn is_running(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -35,6 +35,12 @@ impl Cookie<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Cookie<'static> {
|
||||||
|
Cookie {
|
||||||
|
value: self.value.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -30,6 +30,12 @@ impl Drawer<'_> {
|
||||||
|
|
||||||
Ok((input, (Drawer { name: name.into() }, contents)))
|
Ok((input, (Drawer { name: name.into() }, contents)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Drawer<'static> {
|
||||||
|
Drawer {
|
||||||
|
name: self.name.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -43,6 +43,13 @@ impl DynBlock<'_> {
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> DynBlock<'static> {
|
||||||
|
DynBlock {
|
||||||
|
block_name: self.block_name.into_owned().into(),
|
||||||
|
arguments: self.arguments.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -35,6 +35,12 @@ impl FnDef<'_> {
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> FnDef<'static> {
|
||||||
|
FnDef {
|
||||||
|
label: self.label.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -50,6 +50,13 @@ impl FnRef<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> FnRef<'static> {
|
||||||
|
FnRef {
|
||||||
|
label: self.label.into_owned().into(),
|
||||||
|
definition: self.definition.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -49,6 +49,15 @@ impl InlineCall<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> InlineCall<'static> {
|
||||||
|
InlineCall {
|
||||||
|
name: self.name.into_owned().into(),
|
||||||
|
arguments: self.arguments.into_owned().into(),
|
||||||
|
inside_header: self.inside_header.map(Into::into).map(Cow::Owned),
|
||||||
|
end_header: self.end_header.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -40,6 +40,14 @@ impl InlineSrc<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> InlineSrc<'static> {
|
||||||
|
InlineSrc {
|
||||||
|
lang: self.lang.into_owned().into(),
|
||||||
|
options: self.options.map(Into::into).map(Cow::Owned),
|
||||||
|
body: self.body.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -19,6 +19,16 @@ pub struct Keyword<'a> {
|
||||||
pub value: Cow<'a, str>,
|
pub value: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Keyword<'_> {
|
||||||
|
pub fn into_owned(self) -> Keyword<'static> {
|
||||||
|
Keyword {
|
||||||
|
key: self.key.into_owned().into(),
|
||||||
|
optional: self.optional.map(Into::into).map(Cow::Owned),
|
||||||
|
value: self.value.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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)]
|
||||||
|
@ -26,6 +36,14 @@ pub struct BabelCall<'a> {
|
||||||
pub value: Cow<'a, str>,
|
pub value: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BabelCall<'_> {
|
||||||
|
pub fn into_owned(self) -> BabelCall<'static> {
|
||||||
|
BabelCall {
|
||||||
|
value: self.value.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_keyword(input: &str) -> IResult<&str, (&str, Option<&str>, &str)> {
|
pub(crate) fn parse_keyword(input: &str) -> IResult<&str, (&str, Option<&str>, &str)> {
|
||||||
let (input, _) = tag("#+")(input)?;
|
let (input, _) = tag("#+")(input)?;
|
||||||
let (input, key) = take_till(|c: char| c.is_ascii_whitespace() || c == ':' || c == '[')(input)?;
|
let (input, key) = take_till(|c: char| c.is_ascii_whitespace() || c == ':' || c == '[')(input)?;
|
||||||
|
|
|
@ -38,6 +38,13 @@ impl Link<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Link<'static> {
|
||||||
|
Link {
|
||||||
|
path: self.path.into_owned().into(),
|
||||||
|
desc: self.desc.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -106,6 +106,12 @@ impl ListItem<'_> {
|
||||||
&text[off..],
|
&text[off..],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> ListItem<'static> {
|
||||||
|
ListItem {
|
||||||
|
bullet: self.bullet.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -35,6 +35,13 @@ impl Macros<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Macros<'static> {
|
||||||
|
Macros {
|
||||||
|
name: self.name.into_owned().into(),
|
||||||
|
arguments: self.arguments.map(Into::into).map(Cow::Owned),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -24,8 +24,8 @@ mod timestamp;
|
||||||
mod title;
|
mod title;
|
||||||
|
|
||||||
pub(crate) use self::{
|
pub(crate) use self::{
|
||||||
block::parse_block_element, emphasis::parse_emphasis, keyword::parse_keyword, rule::parse_rule,
|
block::parse_block_element, emphasis::parse_emphasis, keyword::parse_keyword,
|
||||||
table::parse_table_el,
|
radio_target::parse_radio_target, rule::parse_rule, table::parse_table_el,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
@ -46,7 +46,6 @@ pub use self::{
|
||||||
list::{List, ListItem},
|
list::{List, ListItem},
|
||||||
macros::Macros,
|
macros::Macros,
|
||||||
planning::Planning,
|
planning::Planning,
|
||||||
radio_target::RadioTarget,
|
|
||||||
snippet::Snippet,
|
snippet::Snippet,
|
||||||
table::{Table, TableRow},
|
table::{Table, TableRow},
|
||||||
target::Target,
|
target::Target,
|
||||||
|
@ -74,7 +73,7 @@ pub enum Element<'a> {
|
||||||
Section,
|
Section,
|
||||||
Clock(Clock<'a>),
|
Clock(Clock<'a>),
|
||||||
Cookie(Cookie<'a>),
|
Cookie(Cookie<'a>),
|
||||||
RadioTarget(RadioTarget),
|
RadioTarget,
|
||||||
Drawer(Drawer<'a>),
|
Drawer(Drawer<'a>),
|
||||||
Document,
|
Document,
|
||||||
DynBlock(DynBlock<'a>),
|
DynBlock(DynBlock<'a>),
|
||||||
|
@ -89,7 +88,7 @@ pub enum Element<'a> {
|
||||||
ListItem(ListItem<'a>),
|
ListItem(ListItem<'a>),
|
||||||
Macros(Macros<'a>),
|
Macros(Macros<'a>),
|
||||||
Snippet(Snippet<'a>),
|
Snippet(Snippet<'a>),
|
||||||
Text { value: &'a str },
|
Text { value: Cow<'a, str> },
|
||||||
Paragraph,
|
Paragraph,
|
||||||
Rule,
|
Rule,
|
||||||
Timestamp(Timestamp<'a>),
|
Timestamp(Timestamp<'a>),
|
||||||
|
@ -110,29 +109,76 @@ pub enum Element<'a> {
|
||||||
|
|
||||||
impl Element<'_> {
|
impl Element<'_> {
|
||||||
pub fn is_container(&self) -> bool {
|
pub fn is_container(&self) -> bool {
|
||||||
|
use Element::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Element::SpecialBlock(_)
|
SpecialBlock(_) | QuoteBlock(_) | CenterBlock(_) | VerseBlock(_) | Bold | Document
|
||||||
| Element::QuoteBlock(_)
|
| DynBlock(_) | Headline | Italic | List(_) | ListItem(_) | Paragraph | Section
|
||||||
| Element::CenterBlock(_)
|
| Strike | Underline | Title(_) | Table(_) | TableRow(_) | TableCell => true,
|
||||||
| Element::VerseBlock(_)
|
|
||||||
| Element::Bold
|
|
||||||
| Element::Document
|
|
||||||
| Element::DynBlock(_)
|
|
||||||
| Element::Headline
|
|
||||||
| Element::Italic
|
|
||||||
| Element::List(_)
|
|
||||||
| Element::ListItem(_)
|
|
||||||
| Element::Paragraph
|
|
||||||
| Element::Section
|
|
||||||
| Element::Strike
|
|
||||||
| Element::Underline
|
|
||||||
| Element::Title(_)
|
|
||||||
| Element::Table(_)
|
|
||||||
| Element::TableRow(_)
|
|
||||||
| Element::TableCell => true,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Element<'static> {
|
||||||
|
use Element::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
SpecialBlock(e) => SpecialBlock(e.into_owned()),
|
||||||
|
QuoteBlock(e) => QuoteBlock(e.into_owned()),
|
||||||
|
CenterBlock(e) => CenterBlock(e.into_owned()),
|
||||||
|
VerseBlock(e) => VerseBlock(e.into_owned()),
|
||||||
|
CommentBlock(e) => CommentBlock(e.into_owned()),
|
||||||
|
ExampleBlock(e) => ExampleBlock(e.into_owned()),
|
||||||
|
ExportBlock(e) => ExportBlock(e.into_owned()),
|
||||||
|
SourceBlock(e) => SourceBlock(e.into_owned()),
|
||||||
|
BabelCall(e) => BabelCall(e.into_owned()),
|
||||||
|
Section => Section,
|
||||||
|
Clock(e) => Clock(e.into_onwed()),
|
||||||
|
Cookie(e) => Cookie(e.into_owned()),
|
||||||
|
RadioTarget => RadioTarget,
|
||||||
|
Drawer(e) => Drawer(e.into_owned()),
|
||||||
|
Document => Document,
|
||||||
|
DynBlock(e) => DynBlock(e.into_owned()),
|
||||||
|
FnDef(e) => FnDef(e.into_owned()),
|
||||||
|
FnRef(e) => FnRef(e.into_owned()),
|
||||||
|
Headline => Headline,
|
||||||
|
InlineCall(e) => InlineCall(e.into_owned()),
|
||||||
|
InlineSrc(e) => InlineSrc(e.into_owned()),
|
||||||
|
Keyword(e) => Keyword(e.into_owned()),
|
||||||
|
Link(e) => Link(e.into_owned()),
|
||||||
|
List(e) => List(e),
|
||||||
|
ListItem(e) => ListItem(e.into_owned()),
|
||||||
|
Macros(e) => Macros(e.into_owned()),
|
||||||
|
Snippet(e) => Snippet(e.into_owned()),
|
||||||
|
Text { value } => Text {
|
||||||
|
value: value.into_owned().into(),
|
||||||
|
},
|
||||||
|
Paragraph => Paragraph,
|
||||||
|
Rule => Rule,
|
||||||
|
Timestamp(e) => Timestamp(e.into_owned()),
|
||||||
|
Target(e) => Target(e.into_owned()),
|
||||||
|
Bold => Bold,
|
||||||
|
Strike => Strike,
|
||||||
|
Italic => Italic,
|
||||||
|
Underline => Underline,
|
||||||
|
Verbatim { value } => Verbatim {
|
||||||
|
value: value.into_owned().into(),
|
||||||
|
},
|
||||||
|
Code { value } => Code {
|
||||||
|
value: value.into_owned().into(),
|
||||||
|
},
|
||||||
|
Comment { value } => Comment {
|
||||||
|
value: value.into_owned().into(),
|
||||||
|
},
|
||||||
|
FixedWidth { value } => FixedWidth {
|
||||||
|
value: value.into_owned().into(),
|
||||||
|
},
|
||||||
|
Title(e) => Title(e.into_owned()),
|
||||||
|
Table(e) => Table(e.into_owned()),
|
||||||
|
TableRow(e) => TableRow(e),
|
||||||
|
TableCell => TableCell,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_from {
|
macro_rules! impl_from {
|
||||||
|
@ -181,7 +227,6 @@ impl_from!(
|
||||||
Table,
|
Table,
|
||||||
Title,
|
Title,
|
||||||
VerseBlock;
|
VerseBlock;
|
||||||
RadioTarget,
|
|
||||||
List,
|
List,
|
||||||
TableRow
|
TableRow
|
||||||
);
|
);
|
||||||
|
|
|
@ -64,6 +64,14 @@ impl Planning<'_> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Planning<'static> {
|
||||||
|
Planning {
|
||||||
|
deadline: self.deadline.map(|x| x.into_owned()),
|
||||||
|
scheduled: self.scheduled.map(|x| x.into_owned()),
|
||||||
|
closed: self.closed.map(|x| x.into_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -5,44 +5,30 @@ use nom::{
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::elements::Element;
|
|
||||||
|
|
||||||
// TODO: text-markup, entities, latex-fragments, subscript and superscript
|
// TODO: text-markup, entities, latex-fragments, subscript and superscript
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
|
||||||
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RadioTarget;
|
|
||||||
|
|
||||||
impl RadioTarget {
|
#[inline]
|
||||||
#[inline]
|
pub(crate) fn parse_radio_target(input: &str) -> IResult<&str, &str> {
|
||||||
pub(crate) fn parse(input: &str) -> IResult<&str, (Element, &str)> {
|
let (input, contents) = delimited(
|
||||||
let (input, contents) = delimited(
|
tag("<<<"),
|
||||||
tag("<<<"),
|
verify(
|
||||||
verify(
|
take_while(|c: char| c != '<' && c != '\n' && c != '>'),
|
||||||
take_while(|c: char| c != '<' && c != '\n' && c != '>'),
|
|s: &str| s.starts_with(|c| c != ' ') && s.ends_with(|c| c != ' '),
|
||||||
|s: &str| s.starts_with(|c| c != ' ') && s.ends_with(|c| c != ' '),
|
),
|
||||||
),
|
tag(">>>"),
|
||||||
tag(">>>"),
|
)(input)?;
|
||||||
)(input)?;
|
|
||||||
|
|
||||||
Ok((input, (Element::RadioTarget(RadioTarget), contents)))
|
Ok((input, contents))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
assert_eq!(
|
assert_eq!(parse_radio_target("<<<target>>>"), Ok(("", "target")));
|
||||||
RadioTarget::parse("<<<target>>>"),
|
assert_eq!(parse_radio_target("<<<tar get>>>"), Ok(("", "tar get")));
|
||||||
Ok(("", (Element::RadioTarget(RadioTarget), "target")))
|
assert!(parse_radio_target("<<<target >>>").is_err());
|
||||||
);
|
assert!(parse_radio_target("<<< target>>>").is_err());
|
||||||
assert_eq!(
|
assert!(parse_radio_target("<<<ta<get>>>").is_err());
|
||||||
RadioTarget::parse("<<<tar get>>>"),
|
assert!(parse_radio_target("<<<ta>get>>>").is_err());
|
||||||
Ok(("", (Element::RadioTarget(RadioTarget), "tar get")))
|
assert!(parse_radio_target("<<<ta\nget>>>").is_err());
|
||||||
);
|
assert!(parse_radio_target("<<<target>>").is_err());
|
||||||
assert!(RadioTarget::parse("<<<target >>>").is_err());
|
|
||||||
assert!(RadioTarget::parse("<<< target>>>").is_err());
|
|
||||||
assert!(RadioTarget::parse("<<<ta<get>>>").is_err());
|
|
||||||
assert!(RadioTarget::parse("<<<ta>get>>>").is_err());
|
|
||||||
assert!(RadioTarget::parse("<<<ta\nget>>>").is_err());
|
|
||||||
assert!(RadioTarget::parse("<<<target>>").is_err());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,13 @@ impl Snippet<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Snippet<'static> {
|
||||||
|
Snippet {
|
||||||
|
name: self.name.into_owned().into(),
|
||||||
|
value: self.value.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -18,6 +18,19 @@ pub enum Table<'a> {
|
||||||
TableEl { value: Cow<'a, str> },
|
TableEl { value: Cow<'a, str> },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Table<'_> {
|
||||||
|
pub fn into_owned(self) -> Table<'static> {
|
||||||
|
match self {
|
||||||
|
Table::Org { tblfm } => Table::Org {
|
||||||
|
tblfm: tblfm.map(Into::into).map(Cow::Owned),
|
||||||
|
},
|
||||||
|
Table::TableEl { value } => Table::TableEl {
|
||||||
|
value: value.into_owned().into(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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))]
|
||||||
|
|
|
@ -33,6 +33,12 @@ impl Target<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Target<'static> {
|
||||||
|
Target {
|
||||||
|
target: self.target.into_owned().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -30,47 +30,17 @@ pub struct Datetime<'a> {
|
||||||
pub minute: Option<u8>,
|
pub minute: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_time(input: &str) -> IResult<&str, (u8, u8)> {
|
impl Datetime<'_> {
|
||||||
let (input, hour) = map_res(take_while_m_n(1, 2, |c: char| c.is_ascii_digit()), |num| {
|
pub fn into_owned(self) -> Datetime<'static> {
|
||||||
u8::from_str_radix(num, 10)
|
|
||||||
})(input)?;
|
|
||||||
let (input, _) = tag(":")(input)?;
|
|
||||||
let (input, minute) = map_res(take(2usize), |num| u8::from_str_radix(num, 10))(input)?;
|
|
||||||
Ok((input, (hour, minute)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_datetime(input: &str) -> IResult<&str, Datetime<'_>> {
|
|
||||||
let parse_u8 = |num| u8::from_str_radix(num, 10);
|
|
||||||
|
|
||||||
let (input, year) = map_res(take(4usize), |num| u16::from_str_radix(num, 10))(input)?;
|
|
||||||
let (input, _) = tag("-")(input)?;
|
|
||||||
let (input, month) = map_res(take(2usize), parse_u8)(input)?;
|
|
||||||
let (input, _) = tag("-")(input)?;
|
|
||||||
let (input, day) = map_res(take(2usize), parse_u8)(input)?;
|
|
||||||
let (input, _) = space1(input)?;
|
|
||||||
let (input, dayname) = take_while(|c: char| {
|
|
||||||
!c.is_ascii_whitespace()
|
|
||||||
&& !c.is_ascii_digit()
|
|
||||||
&& c != '+'
|
|
||||||
&& c != '-'
|
|
||||||
&& c != ']'
|
|
||||||
&& c != '>'
|
|
||||||
})(input)?;
|
|
||||||
let (input, (hour, minute)) = map(opt(preceded(space1, parse_time)), |time| {
|
|
||||||
(time.map(|t| t.0), time.map(|t| t.1))
|
|
||||||
})(input)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
input,
|
|
||||||
Datetime {
|
Datetime {
|
||||||
year,
|
year: self.year,
|
||||||
month,
|
month: self.month,
|
||||||
day,
|
day: self.day,
|
||||||
dayname: dayname.into(),
|
dayname: self.dayname.into_owned().into(),
|
||||||
hour,
|
hour: self.hour,
|
||||||
minute,
|
minute: self.minute,
|
||||||
},
|
}
|
||||||
))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
|
@ -281,6 +251,97 @@ impl Timestamp<'_> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Timestamp<'static> {
|
||||||
|
match self {
|
||||||
|
Timestamp::Active {
|
||||||
|
start,
|
||||||
|
repeater,
|
||||||
|
delay,
|
||||||
|
} => Timestamp::Active {
|
||||||
|
start: start.into_owned(),
|
||||||
|
repeater: repeater.map(Into::into).map(Cow::Owned),
|
||||||
|
delay: delay.map(Into::into).map(Cow::Owned),
|
||||||
|
},
|
||||||
|
Timestamp::Inactive {
|
||||||
|
start,
|
||||||
|
repeater,
|
||||||
|
delay,
|
||||||
|
} => Timestamp::Inactive {
|
||||||
|
start: start.into_owned(),
|
||||||
|
repeater: repeater.map(Into::into).map(Cow::Owned),
|
||||||
|
delay: delay.map(Into::into).map(Cow::Owned),
|
||||||
|
},
|
||||||
|
Timestamp::ActiveRange {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
repeater,
|
||||||
|
delay,
|
||||||
|
} => Timestamp::ActiveRange {
|
||||||
|
start: start.into_owned(),
|
||||||
|
end: end.into_owned(),
|
||||||
|
repeater: repeater.map(Into::into).map(Cow::Owned),
|
||||||
|
delay: delay.map(Into::into).map(Cow::Owned),
|
||||||
|
},
|
||||||
|
Timestamp::InactiveRange {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
repeater,
|
||||||
|
delay,
|
||||||
|
} => Timestamp::InactiveRange {
|
||||||
|
start: start.into_owned(),
|
||||||
|
end: end.into_owned(),
|
||||||
|
repeater: repeater.map(Into::into).map(Cow::Owned),
|
||||||
|
delay: delay.map(Into::into).map(Cow::Owned),
|
||||||
|
},
|
||||||
|
Timestamp::Diary { value } => Timestamp::Diary {
|
||||||
|
value: value.into_owned().into(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_time(input: &str) -> IResult<&str, (u8, u8)> {
|
||||||
|
let (input, hour) = map_res(take_while_m_n(1, 2, |c: char| c.is_ascii_digit()), |num| {
|
||||||
|
u8::from_str_radix(num, 10)
|
||||||
|
})(input)?;
|
||||||
|
let (input, _) = tag(":")(input)?;
|
||||||
|
let (input, minute) = map_res(take(2usize), |num| u8::from_str_radix(num, 10))(input)?;
|
||||||
|
Ok((input, (hour, minute)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_datetime(input: &str) -> IResult<&str, Datetime<'_>> {
|
||||||
|
let parse_u8 = |num| u8::from_str_radix(num, 10);
|
||||||
|
|
||||||
|
let (input, year) = map_res(take(4usize), |num| u16::from_str_radix(num, 10))(input)?;
|
||||||
|
let (input, _) = tag("-")(input)?;
|
||||||
|
let (input, month) = map_res(take(2usize), parse_u8)(input)?;
|
||||||
|
let (input, _) = tag("-")(input)?;
|
||||||
|
let (input, day) = map_res(take(2usize), parse_u8)(input)?;
|
||||||
|
let (input, _) = space1(input)?;
|
||||||
|
let (input, dayname) = take_while(|c: char| {
|
||||||
|
!c.is_ascii_whitespace()
|
||||||
|
&& !c.is_ascii_digit()
|
||||||
|
&& c != '+'
|
||||||
|
&& c != '-'
|
||||||
|
&& c != ']'
|
||||||
|
&& c != '>'
|
||||||
|
})(input)?;
|
||||||
|
let (input, (hour, minute)) = map(opt(preceded(space1, parse_time)), |time| {
|
||||||
|
(time.map(|t| t.0), time.map(|t| t.1))
|
||||||
|
})(input)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
Datetime {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
dayname: dayname.into(),
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
@ -71,6 +71,26 @@ impl Title<'_> {
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> Title<'static> {
|
||||||
|
Title {
|
||||||
|
level: self.level,
|
||||||
|
priority: self.priority,
|
||||||
|
tags: self
|
||||||
|
.tags
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.into_owned().into())
|
||||||
|
.collect(),
|
||||||
|
keyword: self.keyword.map(Into::into).map(Cow::Owned),
|
||||||
|
raw: self.raw.into_owned().into(),
|
||||||
|
planning: self.planning.map(|p| Box::new(p.into_owned())),
|
||||||
|
properties: self
|
||||||
|
.properties
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| (k.into_owned().into(), v.into_owned().into()))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_headline<'a>(
|
fn parse_headline<'a>(
|
||||||
|
|
|
@ -101,7 +101,7 @@ pub trait HtmlHandler<E: From<Error>> {
|
||||||
Escape(link.desc.as_ref().unwrap_or(&link.path)),
|
Escape(link.desc.as_ref().unwrap_or(&link.path)),
|
||||||
)?,
|
)?,
|
||||||
Macros(_macros) => (),
|
Macros(_macros) => (),
|
||||||
RadioTarget(_radio_target) => (),
|
RadioTarget => (),
|
||||||
Snippet(snippet) => {
|
Snippet(snippet) => {
|
||||||
if snippet.name.eq_ignore_ascii_case("HTML") {
|
if snippet.name.eq_ignore_ascii_case("HTML") {
|
||||||
write!(w, "{}", snippet.value)?;
|
write!(w, "{}", snippet.value)?;
|
||||||
|
|
|
@ -81,7 +81,7 @@ pub trait OrgHandler<E: From<Error>> {
|
||||||
write!(&mut w, "]")?;
|
write!(&mut w, "]")?;
|
||||||
}
|
}
|
||||||
Macros(_macros) => (),
|
Macros(_macros) => (),
|
||||||
RadioTarget(_radio_target) => (),
|
RadioTarget => (),
|
||||||
Snippet(snippet) => write!(w, "@@{}:{}@@", snippet.name, snippet.value)?,
|
Snippet(snippet) => write!(w, "@@{}:{}@@", snippet.name, snippet.value)?,
|
||||||
Target(_target) => (),
|
Target(_target) => (),
|
||||||
Text { value } => write!(w, "{}", value)?,
|
Text { value } => write!(w, "{}", value)?,
|
||||||
|
|
28
src/org.rs
28
src/org.rs
|
@ -5,7 +5,7 @@ use crate::config::ParseConfig;
|
||||||
use crate::elements::Element;
|
use crate::elements::Element;
|
||||||
use crate::export::*;
|
use crate::export::*;
|
||||||
use crate::node::HeadlineNode;
|
use crate::node::HeadlineNode;
|
||||||
use crate::parsers::*;
|
use crate::parsers::{parse_container, Container};
|
||||||
|
|
||||||
pub struct Org<'a> {
|
pub struct Org<'a> {
|
||||||
pub(crate) arena: Arena<Element<'a>>,
|
pub(crate) arena: Arena<Element<'a>>,
|
||||||
|
@ -27,31 +27,7 @@ impl Org<'_> {
|
||||||
let mut arena = Arena::new();
|
let mut arena = Arena::new();
|
||||||
let node = arena.new_node(Element::Document);
|
let node = arena.new_node(Element::Document);
|
||||||
|
|
||||||
let containers = &mut vec![Container::Document { content, node }];
|
parse_container(&mut arena, Container::Document { content, node }, config);
|
||||||
|
|
||||||
while let Some(container) = containers.pop() {
|
|
||||||
match container {
|
|
||||||
Container::Document { content, node } => {
|
|
||||||
parse_section_and_headlines(&mut arena, content, node, containers);
|
|
||||||
}
|
|
||||||
Container::Headline { content, node } => {
|
|
||||||
parse_headline_content(&mut arena, content, node, containers, config);
|
|
||||||
}
|
|
||||||
Container::Block { content, node } => {
|
|
||||||
parse_blocks(&mut arena, content, node, containers);
|
|
||||||
}
|
|
||||||
Container::Inline { content, node } => {
|
|
||||||
parse_inlines(&mut arena, content, node, containers);
|
|
||||||
}
|
|
||||||
Container::List {
|
|
||||||
content,
|
|
||||||
node,
|
|
||||||
indent,
|
|
||||||
} => {
|
|
||||||
parse_list_items(&mut arena, content, indent, node, containers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Org { arena, root: node }
|
Org { arena, root: node }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// parser related functions
|
// parser related functions
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use indextree::{Arena, NodeId};
|
use indextree::{Arena, NodeId};
|
||||||
use jetscii::bytes;
|
use jetscii::bytes;
|
||||||
|
@ -80,6 +81,38 @@ pub enum Container<'a> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_container<'a, T: ElementArena<'a>>(
|
||||||
|
arena: &mut T,
|
||||||
|
container: Container<'a>,
|
||||||
|
config: &ParseConfig,
|
||||||
|
) {
|
||||||
|
let containers = &mut vec![container];
|
||||||
|
|
||||||
|
while let Some(container) = containers.pop() {
|
||||||
|
match container {
|
||||||
|
Container::Document { content, node } => {
|
||||||
|
parse_section_and_headlines(arena, content, node, containers);
|
||||||
|
}
|
||||||
|
Container::Headline { content, node } => {
|
||||||
|
parse_headline_content(arena, content, node, containers, config);
|
||||||
|
}
|
||||||
|
Container::Block { content, node } => {
|
||||||
|
parse_blocks(arena, content, node, containers);
|
||||||
|
}
|
||||||
|
Container::Inline { content, node } => {
|
||||||
|
parse_inlines(arena, content, node, containers);
|
||||||
|
}
|
||||||
|
Container::List {
|
||||||
|
content,
|
||||||
|
node,
|
||||||
|
indent,
|
||||||
|
} => {
|
||||||
|
parse_list_items(arena, content, indent, node, containers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_headline_content<'a, T: ElementArena<'a>>(
|
pub fn parse_headline_content<'a, T: ElementArena<'a>>(
|
||||||
arena: &mut T,
|
arena: &mut T,
|
||||||
content: &'a str,
|
content: &'a str,
|
||||||
|
@ -397,7 +430,12 @@ pub fn parse_inlines<'a, T: ElementArena<'a>>(
|
||||||
|
|
||||||
macro_rules! insert_text {
|
macro_rules! insert_text {
|
||||||
($value:expr) => {
|
($value:expr) => {
|
||||||
arena.insert_before_last_child(Element::Text { value: $value }, parent);
|
arena.insert_before_last_child(
|
||||||
|
Element::Text {
|
||||||
|
value: $value.into(),
|
||||||
|
},
|
||||||
|
parent,
|
||||||
|
);
|
||||||
pos = 0;
|
pos = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -449,7 +487,7 @@ pub fn parse_inlines<'a, T: ElementArena<'a>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
arena.push_element(Element::Text { value: text }, parent);
|
arena.push_element(Element::Text { value: text.into() }, parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,8 +516,8 @@ pub fn parse_inline<'a, T: ElementArena<'a>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b'<' => {
|
b'<' => {
|
||||||
if let Ok((tail, (radio, _content))) = RadioTarget::parse(contents) {
|
if let Ok((tail, _content)) = parse_radio_target(contents) {
|
||||||
arena.push_element(radio, parent);
|
arena.push_element(Element::RadioTarget, parent);
|
||||||
return Some(tail);
|
return Some(tail);
|
||||||
} else if let Ok((tail, target)) = Target::parse(contents) {
|
} else if let Ok((tail, target)) = Target::parse(contents) {
|
||||||
arena.push_element(target, parent);
|
arena.push_element(target, parent);
|
||||||
|
@ -717,7 +755,7 @@ pub fn take_one_word(input: &str) -> IResult<&str, &str> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_skip_empty_lines() {
|
pub fn test_skip_empty_lines() {
|
||||||
assert_eq!(skip_empty_lines("foo"), "foo");
|
assert_eq!(skip_empty_lines("foo"), "foo");
|
||||||
assert_eq!(skip_empty_lines(" foo"), " foo");
|
assert_eq!(skip_empty_lines(" foo"), " foo");
|
||||||
assert_eq!(skip_empty_lines(" \nfoo\n"), "foo\n");
|
assert_eq!(skip_empty_lines(" \nfoo\n"), "foo\n");
|
||||||
|
|
Loading…
Reference in a new issue