refactor: cleanup parse functions
This commit is contained in:
parent
d9fb9aadcb
commit
2128e86b81
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::tag_no_case,
|
bytes::complete::tag_no_case,
|
||||||
character::complete::{alpha1, space0},
|
character::complete::{alpha1, space0},
|
||||||
error::ParseError,
|
|
||||||
sequence::preceded,
|
sequence::preceded,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -11,136 +10,6 @@ use nom::{
|
||||||
use crate::elements::Element;
|
use crate::elements::Element;
|
||||||
use crate::parse::combinators::{blank_lines_count, line, lines_till};
|
use crate::parse::combinators::{blank_lines_count, line, lines_till};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
|
||||||
pub(crate) struct RawBlock<'a> {
|
|
||||||
pub name: &'a str,
|
|
||||||
pub arguments: &'a str,
|
|
||||||
|
|
||||||
pub pre_blank: usize,
|
|
||||||
pub contents: &'a str,
|
|
||||||
pub contents_without_blank_lines: &'a str,
|
|
||||||
|
|
||||||
pub post_blank: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RawBlock<'a> {
|
|
||||||
pub fn parse(input: &'a str) -> Option<(&str, RawBlock)> {
|
|
||||||
Self::parse_internal::<()>(input).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_internal<E>(input: &'a str) -> IResult<&str, RawBlock, E>
|
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, _) = space0(input)?;
|
|
||||||
let (input, name) = preceded(tag_no_case("#+BEGIN_"), alpha1)(input)?;
|
|
||||||
let (input, arguments) = line(input)?;
|
|
||||||
let end_line = format!("#+END_{}", name);
|
|
||||||
let (input, contents) =
|
|
||||||
lines_till(|line| line.trim().eq_ignore_ascii_case(&end_line))(input)?;
|
|
||||||
let (contents_without_blank_lines, pre_blank) = blank_lines_count(contents)?;
|
|
||||||
let (input, post_blank) = blank_lines_count(input)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
input,
|
|
||||||
RawBlock {
|
|
||||||
name,
|
|
||||||
contents,
|
|
||||||
arguments: arguments.trim(),
|
|
||||||
pre_blank,
|
|
||||||
contents_without_blank_lines,
|
|
||||||
post_blank,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_element(self) -> (Element<'a>, &'a str) {
|
|
||||||
let RawBlock {
|
|
||||||
name,
|
|
||||||
contents,
|
|
||||||
arguments,
|
|
||||||
pre_blank,
|
|
||||||
contents_without_blank_lines,
|
|
||||||
post_blank,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let arguments: Option<Cow<'a, str>> = if arguments.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(arguments.into())
|
|
||||||
};
|
|
||||||
|
|
||||||
let element = match &*name.to_uppercase() {
|
|
||||||
"CENTER" => CenterBlock {
|
|
||||||
parameters: arguments,
|
|
||||||
pre_blank,
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
"QUOTE" => QuoteBlock {
|
|
||||||
parameters: arguments,
|
|
||||||
pre_blank,
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
"VERSE" => VerseBlock {
|
|
||||||
parameters: arguments,
|
|
||||||
pre_blank,
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
"COMMENT" => CommentBlock {
|
|
||||||
data: arguments,
|
|
||||||
contents: contents.into(),
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
"EXAMPLE" => ExampleBlock {
|
|
||||||
data: arguments,
|
|
||||||
contents: contents.into(),
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
"EXPORT" => ExportBlock {
|
|
||||||
data: arguments.unwrap_or_default(),
|
|
||||||
contents: contents.into(),
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
"SRC" => {
|
|
||||||
let (language, arguments) = match &arguments {
|
|
||||||
Some(Cow::Borrowed(args)) => {
|
|
||||||
let (language, arguments) =
|
|
||||||
args.split_at(args.find(' ').unwrap_or_else(|| args.len()));
|
|
||||||
(language.into(), arguments.into())
|
|
||||||
}
|
|
||||||
None => (Cow::Borrowed(""), Cow::Borrowed("")),
|
|
||||||
_ => unreachable!(
|
|
||||||
"`parse_block_element` returns `Some(Cow::Borrowed)` or `None`"
|
|
||||||
),
|
|
||||||
};
|
|
||||||
SourceBlock {
|
|
||||||
arguments,
|
|
||||||
language,
|
|
||||||
contents: contents.into(),
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
_ => SpecialBlock {
|
|
||||||
parameters: arguments,
|
|
||||||
name: name.into(),
|
|
||||||
pre_blank,
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
(element, contents_without_blank_lines)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Special Block Element
|
/// Special Block Element
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
@ -351,16 +220,140 @@ impl SourceBlock<'_> {
|
||||||
// TODO: fn retain_labels() -> bool { }
|
// TODO: fn retain_labels() -> bool { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
pub(crate) struct RawBlock<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub arguments: &'a str,
|
||||||
|
|
||||||
|
pub pre_blank: usize,
|
||||||
|
pub contents: &'a str,
|
||||||
|
pub contents_without_blank_lines: &'a str,
|
||||||
|
|
||||||
|
pub post_blank: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RawBlock<'a> {
|
||||||
|
pub fn parse(input: &str) -> Option<(&str, RawBlock)> {
|
||||||
|
parse_internal(input).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_element(self) -> (Element<'a>, &'a str) {
|
||||||
|
let RawBlock {
|
||||||
|
name,
|
||||||
|
contents,
|
||||||
|
arguments,
|
||||||
|
pre_blank,
|
||||||
|
contents_without_blank_lines,
|
||||||
|
post_blank,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let arguments: Option<Cow<'a, str>> = if arguments.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(arguments.into())
|
||||||
|
};
|
||||||
|
|
||||||
|
let element = match &*name.to_uppercase() {
|
||||||
|
"CENTER" => CenterBlock {
|
||||||
|
parameters: arguments,
|
||||||
|
pre_blank,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
"QUOTE" => QuoteBlock {
|
||||||
|
parameters: arguments,
|
||||||
|
pre_blank,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
"VERSE" => VerseBlock {
|
||||||
|
parameters: arguments,
|
||||||
|
pre_blank,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
"COMMENT" => CommentBlock {
|
||||||
|
data: arguments,
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
"EXAMPLE" => ExampleBlock {
|
||||||
|
data: arguments,
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
"EXPORT" => ExportBlock {
|
||||||
|
data: arguments.unwrap_or_default(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
"SRC" => {
|
||||||
|
let (language, arguments) = match &arguments {
|
||||||
|
Some(Cow::Borrowed(args)) => {
|
||||||
|
let (language, arguments) =
|
||||||
|
args.split_at(args.find(' ').unwrap_or_else(|| args.len()));
|
||||||
|
(language.into(), arguments.into())
|
||||||
|
}
|
||||||
|
None => (Cow::Borrowed(""), Cow::Borrowed("")),
|
||||||
|
_ => unreachable!(
|
||||||
|
"`parse_block_element` returns `Some(Cow::Borrowed)` or `None`"
|
||||||
|
),
|
||||||
|
};
|
||||||
|
SourceBlock {
|
||||||
|
arguments,
|
||||||
|
language,
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
_ => SpecialBlock {
|
||||||
|
parameters: arguments,
|
||||||
|
name: name.into(),
|
||||||
|
pre_blank,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
(element, contents_without_blank_lines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_internal(input: &str) -> IResult<&str, RawBlock, ()> {
|
||||||
|
let (input, _) = space0(input)?;
|
||||||
|
let (input, name) = preceded(tag_no_case("#+BEGIN_"), alpha1)(input)?;
|
||||||
|
let (input, arguments) = line(input)?;
|
||||||
|
let end_line = format!("#+END_{}", name);
|
||||||
|
let (input, contents) = lines_till(|line| line.trim().eq_ignore_ascii_case(&end_line))(input)?;
|
||||||
|
let (contents_without_blank_lines, pre_blank) = blank_lines_count(contents)?;
|
||||||
|
let (input, post_blank) = blank_lines_count(input)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
RawBlock {
|
||||||
|
name,
|
||||||
|
contents,
|
||||||
|
arguments: arguments.trim(),
|
||||||
|
pre_blank,
|
||||||
|
contents_without_blank_lines,
|
||||||
|
post_blank,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawBlock::parse_internal::<VerboseError<&str>>(
|
RawBlock::parse(
|
||||||
r#"#+BEGIN_SRC
|
r#"#+BEGIN_SRC
|
||||||
#+END_SRC"#
|
#+END_SRC"#
|
||||||
),
|
),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawBlock {
|
RawBlock {
|
||||||
contents: "",
|
contents: "",
|
||||||
|
@ -374,11 +367,11 @@ fn parse() {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawBlock::parse_internal::<VerboseError<&str>>(
|
RawBlock::parse(
|
||||||
r#"#+begin_src
|
r#"#+begin_src
|
||||||
#+end_src"#
|
#+end_src"#
|
||||||
),
|
),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawBlock {
|
RawBlock {
|
||||||
contents: "",
|
contents: "",
|
||||||
|
@ -392,14 +385,14 @@ fn parse() {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawBlock::parse_internal::<VerboseError<&str>>(
|
RawBlock::parse(
|
||||||
r#"#+BEGIN_SRC javascript
|
r#"#+BEGIN_SRC javascript
|
||||||
console.log('Hello World!');
|
console.log('Hello World!');
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawBlock {
|
RawBlock {
|
||||||
contents: "console.log('Hello World!');\n",
|
contents: "console.log('Hello World!');\n",
|
||||||
|
|
|
@ -4,7 +4,6 @@ use nom::{
|
||||||
bytes::complete::tag,
|
bytes::complete::tag,
|
||||||
character::complete::{char, digit1, space0},
|
character::complete::{char, digit1, space0},
|
||||||
combinator::recognize,
|
combinator::recognize,
|
||||||
error::ParseError,
|
|
||||||
sequence::separated_pair,
|
sequence::separated_pair,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -50,7 +49,7 @@ pub enum Clock<'a> {
|
||||||
|
|
||||||
impl Clock<'_> {
|
impl Clock<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, Clock)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, Clock)> {
|
||||||
parse_clock::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_onwed(self) -> Clock<'static> {
|
pub fn into_onwed(self) -> Clock<'static> {
|
||||||
|
@ -137,7 +136,7 @@ impl Clock<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_clock<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Clock, E> {
|
fn parse_internal(input: &str) -> IResult<&str, Clock, ()> {
|
||||||
let (input, _) = space0(input)?;
|
let (input, _) = space0(input)?;
|
||||||
let (input, _) = tag("CLOCK:")(input)?;
|
let (input, _) = tag("CLOCK:")(input)?;
|
||||||
let (input, _) = space0(input)?;
|
let (input, _) = space0(input)?;
|
||||||
|
@ -193,11 +192,9 @@ fn parse_clock<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Cloc
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_clock::<VerboseError<&str>>("CLOCK: [2003-09-16 Tue 09:39]"),
|
Clock::parse("CLOCK: [2003-09-16 Tue 09:39]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Clock::Running {
|
Clock::Running {
|
||||||
start: Datetime {
|
start: Datetime {
|
||||||
|
@ -215,10 +212,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_clock::<VerboseError<&str>>(
|
Clock::parse("CLOCK: [2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39] => 1:00\n\n"),
|
||||||
"CLOCK: [2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39] => 1:00\n\n"
|
Some((
|
||||||
),
|
|
||||||
Ok((
|
|
||||||
"",
|
"",
|
||||||
Clock::Closed {
|
Clock::Closed {
|
||||||
start: Datetime {
|
start: Datetime {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
error::{ErrorKind, ParseError},
|
error::{make_error, ErrorKind},
|
||||||
Err, IResult,
|
Err, IResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,32 +19,7 @@ pub struct Comment<'a> {
|
||||||
|
|
||||||
impl Comment<'_> {
|
impl Comment<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, Comment)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, Comment)> {
|
||||||
Self::parse_internal::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_internal<'a, E>(input: &'a str) -> IResult<&str, Comment, E>
|
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, value) = lines_while(|line| {
|
|
||||||
let line = line.trim_start();
|
|
||||||
line == "#" || line.starts_with("# ")
|
|
||||||
})(input)?;
|
|
||||||
|
|
||||||
if value.is_empty() {
|
|
||||||
// TODO: better error kind
|
|
||||||
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Many0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (input, post_blank) = blank_lines_count(input)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
input,
|
|
||||||
Comment {
|
|
||||||
value: value.into(),
|
|
||||||
post_blank,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> Comment<'static> {
|
pub fn into_owned(self) -> Comment<'static> {
|
||||||
|
@ -54,3 +29,25 @@ impl Comment<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_internal(input: &str) -> IResult<&str, Comment, ()> {
|
||||||
|
let (input, value) = lines_while(|line| {
|
||||||
|
let line = line.trim_start();
|
||||||
|
line == "#" || line.starts_with("# ")
|
||||||
|
})(input)?;
|
||||||
|
|
||||||
|
if value.is_empty() {
|
||||||
|
// TODO: better error kind
|
||||||
|
return Err(Err::Error(make_error(input, ErrorKind::Many0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (input, post_blank) = blank_lines_count(input)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
Comment {
|
||||||
|
value: value.into(),
|
||||||
|
post_blank,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ use nom::{
|
||||||
bytes::complete::tag,
|
bytes::complete::tag,
|
||||||
character::complete::digit0,
|
character::complete::digit0,
|
||||||
combinator::recognize,
|
combinator::recognize,
|
||||||
error::ParseError,
|
|
||||||
sequence::{delimited, pair, separated_pair},
|
sequence::{delimited, pair, separated_pair},
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -21,7 +20,7 @@ pub struct Cookie<'a> {
|
||||||
|
|
||||||
impl Cookie<'_> {
|
impl Cookie<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, Cookie)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, Cookie)> {
|
||||||
parse_cookie::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> Cookie<'static> {
|
pub fn into_owned(self) -> Cookie<'static> {
|
||||||
|
@ -32,7 +31,7 @@ impl Cookie<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_cookie<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Cookie, E> {
|
fn parse_internal(input: &str) -> IResult<&str, Cookie, ()> {
|
||||||
let (input, value) = recognize(delimited(
|
let (input, value) = recognize(delimited(
|
||||||
tag("["),
|
tag("["),
|
||||||
alt((
|
alt((
|
||||||
|
@ -52,11 +51,9 @@ fn parse_cookie<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Coo
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_cookie::<VerboseError<&str>>("[1/10]"),
|
Cookie::parse("[1/10]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Cookie {
|
Cookie {
|
||||||
value: "[1/10]".into()
|
value: "[1/10]".into()
|
||||||
|
@ -64,8 +61,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_cookie::<VerboseError<&str>>("[1/1000]"),
|
Cookie::parse("[1/1000]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Cookie {
|
Cookie {
|
||||||
value: "[1/1000]".into()
|
value: "[1/1000]".into()
|
||||||
|
@ -73,8 +70,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_cookie::<VerboseError<&str>>("[10%]"),
|
Cookie::parse("[10%]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Cookie {
|
Cookie {
|
||||||
value: "[10%]".into()
|
value: "[10%]".into()
|
||||||
|
@ -82,8 +79,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_cookie::<VerboseError<&str>>("[%]"),
|
Cookie::parse("[%]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Cookie {
|
Cookie {
|
||||||
value: "[%]".into()
|
value: "[%]".into()
|
||||||
|
@ -91,8 +88,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_cookie::<VerboseError<&str>>("[/]"),
|
Cookie::parse("[/]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Cookie {
|
Cookie {
|
||||||
value: "[/]".into()
|
value: "[/]".into()
|
||||||
|
@ -100,8 +97,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_cookie::<VerboseError<&str>>("[100/]"),
|
Cookie::parse("[100/]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Cookie {
|
Cookie {
|
||||||
value: "[100/]".into()
|
value: "[100/]".into()
|
||||||
|
@ -109,8 +106,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_cookie::<VerboseError<&str>>("[/100]"),
|
Cookie::parse("[/100]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Cookie {
|
Cookie {
|
||||||
value: "[/100]".into()
|
value: "[/100]".into()
|
||||||
|
@ -118,8 +115,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(parse_cookie::<VerboseError<&str>>("[10% ]").is_err());
|
assert!(Cookie::parse("[10% ]").is_none());
|
||||||
assert!(parse_cookie::<VerboseError<&str>>("[1//100]").is_err());
|
assert!(Cookie::parse("[1//100]").is_none());
|
||||||
assert!(parse_cookie::<VerboseError<&str>>("[1\\100]").is_err());
|
assert!(Cookie::parse("[1\\100]").is_none());
|
||||||
assert!(parse_cookie::<VerboseError<&str>>("[10%%]").is_err());
|
assert!(Cookie::parse("[10%%]").is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take_while1},
|
bytes::complete::{tag, take_while1},
|
||||||
character::complete::space0,
|
character::complete::space0,
|
||||||
error::ParseError,
|
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -27,7 +26,7 @@ pub struct Drawer<'a> {
|
||||||
|
|
||||||
impl Drawer<'_> {
|
impl Drawer<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, (Drawer, &str))> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, (Drawer, &str))> {
|
||||||
parse_drawer::<()>(input).ok()
|
parse_drawer(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> Drawer<'static> {
|
pub fn into_owned(self) -> Drawer<'static> {
|
||||||
|
@ -40,10 +39,7 @@ impl Drawer<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse_drawer<'a, E>(input: &'a str) -> IResult<&str, (Drawer, &str), E>
|
pub fn parse_drawer(input: &str) -> IResult<&str, (Drawer, &str), ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, (mut drawer, content)) = parse_drawer_without_blank(input)?;
|
let (input, (mut drawer, content)) = parse_drawer_without_blank(input)?;
|
||||||
|
|
||||||
let (content, blank) = blank_lines_count(content)?;
|
let (content, blank) = blank_lines_count(content)?;
|
||||||
|
@ -55,10 +51,7 @@ where
|
||||||
Ok((input, (drawer, content)))
|
Ok((input, (drawer, content)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_drawer_without_blank<'a, E>(input: &'a str) -> IResult<&str, (Drawer, &str), E>
|
pub fn parse_drawer_without_blank(input: &str) -> IResult<&str, (Drawer, &str), ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, _) = space0(input)?;
|
let (input, _) = space0(input)?;
|
||||||
let (input, name) = delimited(
|
let (input, name) = delimited(
|
||||||
tag(":"),
|
tag(":"),
|
||||||
|
@ -83,10 +76,8 @@ where
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_drawer::<VerboseError<&str>>(
|
parse_drawer(
|
||||||
r#":PROPERTIES:
|
r#":PROPERTIES:
|
||||||
:CUSTOM_ID: id
|
:CUSTOM_ID: id
|
||||||
:END:"#
|
:END:"#
|
||||||
|
@ -104,7 +95,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_drawer::<VerboseError<&str>>(
|
parse_drawer(
|
||||||
r#":PROPERTIES:
|
r#":PROPERTIES:
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,5 +117,5 @@ fn parse() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// https://github.com/PoiScript/orgize/issues/9
|
// https://github.com/PoiScript/orgize/issues/9
|
||||||
assert!(parse_drawer::<()>(":SPAGHETTI:\n").is_err());
|
assert!(parse_drawer(":SPAGHETTI:\n").is_err());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::tag_no_case,
|
bytes::complete::tag_no_case,
|
||||||
character::complete::{alpha1, space0, space1},
|
character::complete::{alpha1, space0, space1},
|
||||||
error::ParseError,
|
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ pub struct DynBlock<'a> {
|
||||||
|
|
||||||
impl DynBlock<'_> {
|
impl DynBlock<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, (DynBlock, &str))> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, (DynBlock, &str))> {
|
||||||
parse_dyn_block::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> DynBlock<'static> {
|
pub fn into_owned(self) -> DynBlock<'static> {
|
||||||
|
@ -43,10 +42,7 @@ impl DynBlock<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_dyn_block<'a, E>(input: &'a str) -> IResult<&str, (DynBlock, &str), E>
|
fn parse_internal(input: &str) -> IResult<&str, (DynBlock, &str), ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, _) = space0(input)?;
|
let (input, _) = space0(input)?;
|
||||||
let (input, _) = tag_no_case("#+BEGIN:")(input)?;
|
let (input, _) = tag_no_case("#+BEGIN:")(input)?;
|
||||||
let (input, _) = space1(input)?;
|
let (input, _) = space1(input)?;
|
||||||
|
@ -76,11 +72,9 @@ where
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
// TODO: testing
|
// TODO: testing
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_dyn_block::<VerboseError<&str>>(
|
DynBlock::parse(
|
||||||
r#"#+BEGIN: clocktable :scope file
|
r#"#+BEGIN: clocktable :scope file
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +83,7 @@ CONTENTS
|
||||||
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
DynBlock {
|
DynBlock {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
error::{ErrorKind, ParseError},
|
error::{make_error, ErrorKind},
|
||||||
Err, IResult,
|
Err, IResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,32 +20,7 @@ pub struct FixedWidth<'a> {
|
||||||
|
|
||||||
impl FixedWidth<'_> {
|
impl FixedWidth<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, FixedWidth)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, FixedWidth)> {
|
||||||
Self::parse_internal::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_internal<'a, E>(input: &'a str) -> IResult<&str, FixedWidth, E>
|
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, value) = lines_while(|line| {
|
|
||||||
let line = line.trim_start();
|
|
||||||
line == ":" || line.starts_with(": ")
|
|
||||||
})(input)?;
|
|
||||||
|
|
||||||
if value.is_empty() {
|
|
||||||
// TODO: better error kind
|
|
||||||
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Many0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (input, post_blank) = blank_lines_count(input)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
input,
|
|
||||||
FixedWidth {
|
|
||||||
value: value.into(),
|
|
||||||
post_blank,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> FixedWidth<'static> {
|
pub fn into_owned(self) -> FixedWidth<'static> {
|
||||||
|
@ -56,6 +31,28 @@ impl FixedWidth<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_internal(input: &str) -> IResult<&str, FixedWidth, ()> {
|
||||||
|
let (input, value) = lines_while(|line| {
|
||||||
|
let line = line.trim_start();
|
||||||
|
line == ":" || line.starts_with(": ")
|
||||||
|
})(input)?;
|
||||||
|
|
||||||
|
if value.is_empty() {
|
||||||
|
// TODO: better error kind
|
||||||
|
return Err(Err::Error(make_error(input, ErrorKind::Many0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (input, post_blank) = blank_lines_count(input)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
FixedWidth {
|
||||||
|
value: value.into(),
|
||||||
|
post_blank,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take_while1},
|
bytes::complete::{tag, take_while1},
|
||||||
error::ParseError,
|
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -23,33 +22,7 @@ pub struct FnDef<'a> {
|
||||||
|
|
||||||
impl FnDef<'_> {
|
impl FnDef<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, (FnDef, &str))> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, (FnDef, &str))> {
|
||||||
Self::parse_internal::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_internal<'a, E>(input: &'a str) -> IResult<&str, (FnDef, &str), E>
|
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, label) = delimited(
|
|
||||||
tag("[fn:"),
|
|
||||||
take_while1(|c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_'),
|
|
||||||
tag("]"),
|
|
||||||
)(input)?;
|
|
||||||
|
|
||||||
let (input, content) = line(input)?;
|
|
||||||
|
|
||||||
let (input, post_blank) = blank_lines_count(input)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
input,
|
|
||||||
(
|
|
||||||
FnDef {
|
|
||||||
label: label.into(),
|
|
||||||
post_blank,
|
|
||||||
},
|
|
||||||
content,
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> FnDef<'static> {
|
pub fn into_owned(self) -> FnDef<'static> {
|
||||||
|
@ -60,13 +33,34 @@ impl FnDef<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_internal(input: &str) -> IResult<&str, (FnDef, &str), ()> {
|
||||||
|
let (input, label) = delimited(
|
||||||
|
tag("[fn:"),
|
||||||
|
take_while1(|c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_'),
|
||||||
|
tag("]"),
|
||||||
|
)(input)?;
|
||||||
|
|
||||||
|
let (input, content) = line(input)?;
|
||||||
|
|
||||||
|
let (input, post_blank) = blank_lines_count(input)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
(
|
||||||
|
FnDef {
|
||||||
|
label: label.into(),
|
||||||
|
post_blank,
|
||||||
|
},
|
||||||
|
content,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
FnDef::parse_internal::<VerboseError<&str>>("[fn:1] https://orgmode.org"),
|
FnDef::parse("[fn:1] https://orgmode.org"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
FnDef {
|
FnDef {
|
||||||
|
@ -78,8 +72,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
FnDef::parse_internal::<VerboseError<&str>>("[fn:word_1] https://orgmode.org"),
|
FnDef::parse("[fn:word_1] https://orgmode.org"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
FnDef {
|
FnDef {
|
||||||
|
@ -91,8 +85,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
FnDef::parse_internal::<VerboseError<&str>>("[fn:WORD-1] https://orgmode.org"),
|
FnDef::parse("[fn:WORD-1] https://orgmode.org"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
FnDef {
|
FnDef {
|
||||||
|
@ -104,8 +98,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
FnDef::parse_internal::<VerboseError<&str>>("[fn:WORD]"),
|
FnDef::parse("[fn:WORD]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
FnDef {
|
FnDef {
|
||||||
|
@ -117,7 +111,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(FnDef::parse_internal::<VerboseError<&str>>("[fn:] https://orgmode.org").is_err());
|
assert!(FnDef::parse("[fn:] https://orgmode.org").is_none());
|
||||||
assert!(FnDef::parse_internal::<VerboseError<&str>>("[fn:wor d] https://orgmode.org").is_err());
|
assert!(FnDef::parse("[fn:wor d] https://orgmode.org").is_none());
|
||||||
assert!(FnDef::parse_internal::<VerboseError<&str>>("[fn:WORD https://orgmode.org").is_err());
|
assert!(FnDef::parse("[fn:WORD https://orgmode.org").is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use memchr::memchr2_iter;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take_while},
|
bytes::complete::{tag, take_while},
|
||||||
combinator::opt,
|
combinator::opt,
|
||||||
error::{ErrorKind, ParseError},
|
error::{make_error, ErrorKind},
|
||||||
sequence::preceded,
|
sequence::preceded,
|
||||||
Err, IResult,
|
Err, IResult,
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,7 @@ pub struct FnRef<'a> {
|
||||||
|
|
||||||
impl FnRef<'_> {
|
impl FnRef<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, FnRef)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, FnRef)> {
|
||||||
parse_fn_ref::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> FnRef<'static> {
|
pub fn into_owned(self) -> FnRef<'static> {
|
||||||
|
@ -34,7 +34,7 @@ impl FnRef<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_fn_ref<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, FnRef, E> {
|
fn parse_internal(input: &str) -> IResult<&str, FnRef, ()> {
|
||||||
let (input, _) = tag("[fn:")(input)?;
|
let (input, _) = tag("[fn:")(input)?;
|
||||||
let (input, label) =
|
let (input, label) =
|
||||||
take_while(|c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_')(input)?;
|
take_while(|c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_')(input)?;
|
||||||
|
@ -50,7 +50,7 @@ fn parse_fn_ref<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, FnR
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn balanced_brackets<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, &str, E> {
|
fn balanced_brackets(input: &str) -> IResult<&str, &str, ()> {
|
||||||
let mut pairs = 1;
|
let mut pairs = 1;
|
||||||
for i in memchr2_iter(b'[', b']', input.as_bytes()) {
|
for i in memchr2_iter(b'[', b']', input.as_bytes()) {
|
||||||
if input.as_bytes()[i] == b'[' {
|
if input.as_bytes()[i] == b'[' {
|
||||||
|
@ -61,16 +61,14 @@ fn balanced_brackets<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str
|
||||||
return Ok((&input[i..], &input[0..i]));
|
return Ok((&input[i..], &input[0..i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(Err::Error(E::from_error_kind(input, ErrorKind::Tag)))
|
Err(Err::Error(make_error(input, ErrorKind::Tag)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_fn_ref::<VerboseError<&str>>("[fn:1]"),
|
FnRef::parse("[fn:1]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
FnRef {
|
FnRef {
|
||||||
label: "1".into(),
|
label: "1".into(),
|
||||||
|
@ -79,8 +77,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_fn_ref::<VerboseError<&str>>("[fn:1:2]"),
|
FnRef::parse("[fn:1:2]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
FnRef {
|
FnRef {
|
||||||
label: "1".into(),
|
label: "1".into(),
|
||||||
|
@ -89,8 +87,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_fn_ref::<VerboseError<&str>>("[fn::2]"),
|
FnRef::parse("[fn::2]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
FnRef {
|
FnRef {
|
||||||
label: "".into(),
|
label: "".into(),
|
||||||
|
@ -99,8 +97,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_fn_ref::<VerboseError<&str>>("[fn::[]]"),
|
FnRef::parse("[fn::[]]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
FnRef {
|
FnRef {
|
||||||
label: "".into(),
|
label: "".into(),
|
||||||
|
@ -109,5 +107,5 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(parse_fn_ref::<VerboseError<&str>>("[fn::[]").is_err());
|
assert!(FnRef::parse("[fn::[]").is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take_till},
|
bytes::complete::{tag, take_till},
|
||||||
combinator::opt,
|
combinator::opt,
|
||||||
error::ParseError,
|
|
||||||
sequence::{delimited, preceded},
|
sequence::{delimited, preceded},
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -27,7 +26,7 @@ pub struct InlineCall<'a> {
|
||||||
|
|
||||||
impl InlineCall<'_> {
|
impl InlineCall<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, InlineCall)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, InlineCall)> {
|
||||||
parse_inline_call::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> InlineCall<'static> {
|
pub fn into_owned(self) -> InlineCall<'static> {
|
||||||
|
@ -41,7 +40,7 @@ impl InlineCall<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_inline_call<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, InlineCall, E> {
|
fn parse_internal(input: &str) -> IResult<&str, InlineCall, ()> {
|
||||||
let (input, name) = preceded(
|
let (input, name) = preceded(
|
||||||
tag("call_"),
|
tag("call_"),
|
||||||
take_till(|c| c == '[' || c == '\n' || c == '(' || c == ')'),
|
take_till(|c| c == '[' || c == '\n' || c == '(' || c == ')'),
|
||||||
|
@ -72,11 +71,9 @@ fn parse_inline_call<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inline_call::<VerboseError<&str>>("call_square(4)"),
|
InlineCall::parse("call_square(4)"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
InlineCall {
|
InlineCall {
|
||||||
name: "square".into(),
|
name: "square".into(),
|
||||||
|
@ -87,8 +84,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inline_call::<VerboseError<&str>>("call_square[:results output](4)"),
|
InlineCall::parse("call_square[:results output](4)"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
InlineCall {
|
InlineCall {
|
||||||
name: "square".into(),
|
name: "square".into(),
|
||||||
|
@ -99,8 +96,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inline_call::<VerboseError<&str>>("call_square(4)[:results html]"),
|
InlineCall::parse("call_square(4)[:results html]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
InlineCall {
|
InlineCall {
|
||||||
name: "square".into(),
|
name: "square".into(),
|
||||||
|
@ -111,8 +108,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inline_call::<VerboseError<&str>>("call_square[:results output](4)[:results html]"),
|
InlineCall::parse("call_square[:results output](4)[:results html]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
InlineCall {
|
InlineCall {
|
||||||
name: "square".into(),
|
name: "square".into(),
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take_till, take_while1},
|
bytes::complete::{tag, take_till, take_while1},
|
||||||
combinator::opt,
|
combinator::opt,
|
||||||
error::ParseError,
|
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -24,7 +23,7 @@ pub struct InlineSrc<'a> {
|
||||||
|
|
||||||
impl InlineSrc<'_> {
|
impl InlineSrc<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, InlineSrc)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, InlineSrc)> {
|
||||||
parse_inline_src::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> InlineSrc<'static> {
|
pub fn into_owned(self) -> InlineSrc<'static> {
|
||||||
|
@ -37,7 +36,7 @@ impl InlineSrc<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_inline_src<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, InlineSrc, E> {
|
fn parse_internal(input: &str) -> IResult<&str, InlineSrc, ()> {
|
||||||
let (input, _) = tag("src_")(input)?;
|
let (input, _) = tag("src_")(input)?;
|
||||||
let (input, lang) =
|
let (input, lang) =
|
||||||
take_while1(|c: char| !c.is_ascii_whitespace() && c != '[' && c != '{')(input)?;
|
take_while1(|c: char| !c.is_ascii_whitespace() && c != '[' && c != '{')(input)?;
|
||||||
|
@ -60,11 +59,9 @@ fn parse_inline_src<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str,
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inline_src::<VerboseError<&str>>("src_C{int a = 0;}"),
|
InlineSrc::parse("src_C{int a = 0;}"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
InlineSrc {
|
InlineSrc {
|
||||||
lang: "C".into(),
|
lang: "C".into(),
|
||||||
|
@ -74,8 +71,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inline_src::<VerboseError<&str>>("src_xml[:exports code]{<tag>text</tag>}"),
|
InlineSrc::parse("src_xml[:exports code]{<tag>text</tag>}"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
InlineSrc {
|
InlineSrc {
|
||||||
lang: "xml".into(),
|
lang: "xml".into(),
|
||||||
|
@ -85,11 +82,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(
|
assert!(InlineSrc::parse("src_xml[:exports code]{<tag>text</tag>").is_none());
|
||||||
parse_inline_src::<VerboseError<&str>>("src_xml[:exports code]{<tag>text</tag>").is_err()
|
assert!(InlineSrc::parse("src_[:exports code]{<tag>text</tag>}").is_none());
|
||||||
);
|
assert!(InlineSrc::parse("src_xml[:exports code]").is_none());
|
||||||
assert!(
|
|
||||||
parse_inline_src::<VerboseError<&str>>("src_[:exports code]{<tag>text</tag>}").is_err()
|
|
||||||
);
|
|
||||||
assert!(parse_inline_src::<VerboseError<&str>>("src_xml[:exports code]").is_err());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ use nom::{
|
||||||
bytes::complete::{tag, take_till},
|
bytes::complete::{tag, take_till},
|
||||||
character::complete::space0,
|
character::complete::space0,
|
||||||
combinator::opt,
|
combinator::opt,
|
||||||
error::ParseError,
|
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -12,74 +11,6 @@ use nom::{
|
||||||
use crate::elements::Element;
|
use crate::elements::Element;
|
||||||
use crate::parse::combinators::{blank_lines_count, line};
|
use crate::parse::combinators::{blank_lines_count, line};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
|
||||||
pub(crate) struct RawKeyword<'a> {
|
|
||||||
pub key: &'a str,
|
|
||||||
pub value: &'a str,
|
|
||||||
pub optional: Option<&'a str>,
|
|
||||||
pub post_blank: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RawKeyword<'a> {
|
|
||||||
pub fn parse(input: &'a str) -> Option<(&str, RawKeyword)> {
|
|
||||||
Self::parse_internal::<()>(input).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_internal<E>(input: &'a str) -> IResult<&str, RawKeyword, E>
|
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, _) = space0(input)?;
|
|
||||||
let (input, _) = tag("#+")(input)?;
|
|
||||||
let (input, key) =
|
|
||||||
take_till(|c: char| c.is_ascii_whitespace() || c == ':' || c == '[')(input)?;
|
|
||||||
let (input, optional) = opt(delimited(
|
|
||||||
tag("["),
|
|
||||||
take_till(|c| c == ']' || c == '\n'),
|
|
||||||
tag("]"),
|
|
||||||
))(input)?;
|
|
||||||
let (input, _) = tag(":")(input)?;
|
|
||||||
let (input, value) = line(input)?;
|
|
||||||
let (input, post_blank) = blank_lines_count(input)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
input,
|
|
||||||
RawKeyword {
|
|
||||||
key,
|
|
||||||
optional,
|
|
||||||
value: value.trim(),
|
|
||||||
post_blank,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_element(self) -> Element<'a> {
|
|
||||||
let RawKeyword {
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
optional,
|
|
||||||
post_blank,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
if (&*key).eq_ignore_ascii_case("CALL") {
|
|
||||||
BabelCall {
|
|
||||||
value: value.into(),
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
Keyword {
|
|
||||||
key: key.into(),
|
|
||||||
optional: optional.map(Into::into),
|
|
||||||
value: value.into(),
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Keyword Element
|
/// Keyword Element
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
|
#[cfg_attr(feature = "ser", derive(serde::Serialize))]
|
||||||
|
@ -128,13 +59,75 @@ impl BabelCall<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
pub(crate) struct RawKeyword<'a> {
|
||||||
|
pub key: &'a str,
|
||||||
|
pub value: &'a str,
|
||||||
|
pub optional: Option<&'a str>,
|
||||||
|
pub post_blank: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RawKeyword<'a> {
|
||||||
|
pub fn parse(input: &str) -> Option<(&str, RawKeyword)> {
|
||||||
|
parse_internal(input).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_element(self) -> Element<'a> {
|
||||||
|
let RawKeyword {
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
optional,
|
||||||
|
post_blank,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
if (&*key).eq_ignore_ascii_case("CALL") {
|
||||||
|
BabelCall {
|
||||||
|
value: value.into(),
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
Keyword {
|
||||||
|
key: key.into(),
|
||||||
|
optional: optional.map(Into::into),
|
||||||
|
value: value.into(),
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_internal(input: &str) -> IResult<&str, RawKeyword, ()> {
|
||||||
|
let (input, _) = space0(input)?;
|
||||||
|
let (input, _) = tag("#+")(input)?;
|
||||||
|
let (input, key) = take_till(|c: char| c.is_ascii_whitespace() || c == ':' || c == '[')(input)?;
|
||||||
|
let (input, optional) = opt(delimited(
|
||||||
|
tag("["),
|
||||||
|
take_till(|c| c == ']' || c == '\n'),
|
||||||
|
tag("]"),
|
||||||
|
))(input)?;
|
||||||
|
let (input, _) = tag(":")(input)?;
|
||||||
|
let (input, value) = line(input)?;
|
||||||
|
let (input, post_blank) = blank_lines_count(input)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
RawKeyword {
|
||||||
|
key,
|
||||||
|
optional,
|
||||||
|
value: value.trim(),
|
||||||
|
post_blank,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawKeyword::parse_internal::<VerboseError<&str>>("#+KEY:"),
|
RawKeyword::parse("#+KEY:"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawKeyword {
|
RawKeyword {
|
||||||
key: "KEY",
|
key: "KEY",
|
||||||
|
@ -145,8 +138,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawKeyword::parse_internal::<VerboseError<&str>>("#+KEY: VALUE"),
|
RawKeyword::parse("#+KEY: VALUE"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawKeyword {
|
RawKeyword {
|
||||||
key: "KEY",
|
key: "KEY",
|
||||||
|
@ -157,8 +150,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawKeyword::parse_internal::<VerboseError<&str>>("#+K_E_Y: VALUE"),
|
RawKeyword::parse("#+K_E_Y: VALUE"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawKeyword {
|
RawKeyword {
|
||||||
key: "K_E_Y",
|
key: "K_E_Y",
|
||||||
|
@ -169,8 +162,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawKeyword::parse_internal::<VerboseError<&str>>("#+KEY:VALUE\n"),
|
RawKeyword::parse("#+KEY:VALUE\n"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawKeyword {
|
RawKeyword {
|
||||||
key: "KEY",
|
key: "KEY",
|
||||||
|
@ -180,12 +173,12 @@ fn parse() {
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert!(RawKeyword::parse_internal::<VerboseError<&str>>("#+KE Y: VALUE").is_err());
|
assert!(RawKeyword::parse("#+KE Y: VALUE").is_none());
|
||||||
assert!(RawKeyword::parse_internal::<VerboseError<&str>>("#+ KEY: VALUE").is_err());
|
assert!(RawKeyword::parse("#+ KEY: VALUE").is_none());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawKeyword::parse_internal::<VerboseError<&str>>("#+RESULTS:"),
|
RawKeyword::parse("#+RESULTS:"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawKeyword {
|
RawKeyword {
|
||||||
key: "RESULTS",
|
key: "RESULTS",
|
||||||
|
@ -197,8 +190,8 @@ fn parse() {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawKeyword::parse_internal::<VerboseError<&str>>("#+ATTR_LATEX: :width 5cm\n"),
|
RawKeyword::parse("#+ATTR_LATEX: :width 5cm\n"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawKeyword {
|
RawKeyword {
|
||||||
key: "ATTR_LATEX",
|
key: "ATTR_LATEX",
|
||||||
|
@ -210,8 +203,8 @@ fn parse() {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawKeyword::parse_internal::<VerboseError<&str>>("#+CALL: double(n=4)"),
|
RawKeyword::parse("#+CALL: double(n=4)"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
RawKeyword {
|
RawKeyword {
|
||||||
key: "CALL",
|
key: "CALL",
|
||||||
|
@ -223,10 +216,8 @@ fn parse() {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RawKeyword::parse_internal::<VerboseError<&str>>(
|
RawKeyword::parse("#+CAPTION[Short caption]: Longer caption."),
|
||||||
"#+CAPTION[Short caption]: Longer caption."
|
Some((
|
||||||
),
|
|
||||||
Ok((
|
|
||||||
"",
|
"",
|
||||||
RawKeyword {
|
RawKeyword {
|
||||||
key: "CAPTION",
|
key: "CAPTION",
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take_while},
|
bytes::complete::{tag, take_while},
|
||||||
combinator::opt,
|
combinator::opt,
|
||||||
error::ParseError,
|
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -22,7 +21,7 @@ pub struct Link<'a> {
|
||||||
impl Link<'_> {
|
impl Link<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, Link)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, Link)> {
|
||||||
parse_link::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> Link<'static> {
|
pub fn into_owned(self) -> Link<'static> {
|
||||||
|
@ -34,7 +33,7 @@ impl Link<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_link<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Link, E> {
|
fn parse_internal(input: &str) -> IResult<&str, Link, ()> {
|
||||||
let (input, path) = delimited(
|
let (input, path) = delimited(
|
||||||
tag("[["),
|
tag("[["),
|
||||||
take_while(|c: char| c != '<' && c != '>' && c != '\n' && c != ']'),
|
take_while(|c: char| c != '<' && c != '>' && c != '\n' && c != ']'),
|
||||||
|
@ -57,11 +56,9 @@ fn parse_link<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Link,
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_link::<VerboseError<&str>>("[[#id]]"),
|
Link::parse("[[#id]]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Link {
|
Link {
|
||||||
path: "#id".into(),
|
path: "#id".into(),
|
||||||
|
@ -70,8 +67,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_link::<VerboseError<&str>>("[[#id][desc]]"),
|
Link::parse("[[#id][desc]]"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Link {
|
Link {
|
||||||
path: "#id".into(),
|
path: "#id".into(),
|
||||||
|
@ -79,5 +76,5 @@ fn parse() {
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert!(parse_link::<VerboseError<&str>>("[[#id][desc]").is_err());
|
assert!(Link::parse("[[#id][desc]").is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ use nom::{
|
||||||
bytes::complete::tag,
|
bytes::complete::tag,
|
||||||
character::complete::{digit1, space0},
|
character::complete::{digit1, space0},
|
||||||
combinator::{map, recognize},
|
combinator::{map, recognize},
|
||||||
error::ParseError,
|
|
||||||
sequence::terminated,
|
sequence::terminated,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -45,7 +44,7 @@ pub struct ListItem<'a> {
|
||||||
impl ListItem<'_> {
|
impl ListItem<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, (ListItem, &str))> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, (ListItem, &str))> {
|
||||||
list_item::<()>(input).ok()
|
list_item(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> ListItem<'static> {
|
pub fn into_owned(self) -> ListItem<'static> {
|
||||||
|
@ -57,7 +56,7 @@ impl ListItem<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_item<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, (ListItem, &str), E> {
|
fn list_item(input: &str) -> IResult<&str, (ListItem, &str), ()> {
|
||||||
let (input, indent) = map(space0, |s: &str| s.len())(input)?;
|
let (input, indent) = map(space0, |s: &str| s.len())(input)?;
|
||||||
let (input, bullet) = recognize(alt((
|
let (input, bullet) = recognize(alt((
|
||||||
tag("+ "),
|
tag("+ "),
|
||||||
|
@ -122,10 +121,8 @@ fn list_item_contents(input: &str, indent: usize) -> (&str, &str) {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#"+ item1
|
r#"+ item1
|
||||||
+ item2"#
|
+ item2"#
|
||||||
),
|
),
|
||||||
|
@ -143,7 +140,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#"* item1
|
r#"* item1
|
||||||
|
|
||||||
* item2"#
|
* item2"#
|
||||||
|
@ -163,7 +160,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#"* item1
|
r#"* item1
|
||||||
|
|
||||||
|
|
||||||
|
@ -185,7 +182,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#"* item1
|
r#"* item1
|
||||||
|
|
||||||
"#
|
"#
|
||||||
|
@ -205,7 +202,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#"+ item1
|
r#"+ item1
|
||||||
+ item2
|
+ item2
|
||||||
"#
|
"#
|
||||||
|
@ -225,7 +222,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#"+ item1
|
r#"+ item1
|
||||||
|
|
||||||
+ item2
|
+ item2
|
||||||
|
@ -249,7 +246,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#" + item1
|
r#" + item1
|
||||||
|
|
||||||
+ item2"#
|
+ item2"#
|
||||||
|
@ -269,7 +266,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#" 1. item1
|
r#" 1. item1
|
||||||
2. item2
|
2. item2
|
||||||
3. item3"#
|
3. item3"#
|
||||||
|
@ -289,7 +286,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_item::<VerboseError<&str>>(
|
list_item(
|
||||||
r#"+ 1
|
r#"+ 1
|
||||||
|
|
||||||
- 2
|
- 2
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take, take_until, take_while1},
|
bytes::complete::{tag, take, take_until, take_while1},
|
||||||
combinator::{opt, verify},
|
combinator::{opt, verify},
|
||||||
error::ParseError,
|
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -22,7 +21,7 @@ pub struct Macros<'a> {
|
||||||
|
|
||||||
impl Macros<'_> {
|
impl Macros<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, Macros)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, Macros)> {
|
||||||
parse_macros::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> Macros<'static> {
|
pub fn into_owned(self) -> Macros<'static> {
|
||||||
|
@ -34,7 +33,7 @@ impl Macros<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_macros<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Macros, E> {
|
fn parse_internal(input: &str) -> IResult<&str, Macros, ()> {
|
||||||
let (input, _) = tag("{{{")(input)?;
|
let (input, _) = tag("{{{")(input)?;
|
||||||
let (input, name) = verify(
|
let (input, name) = verify(
|
||||||
take_while1(|c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_'),
|
take_while1(|c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_'),
|
||||||
|
@ -53,12 +52,10 @@ fn parse_macros<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Mac
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn test() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_macros::<VerboseError<&str>>("{{{poem(red,blue)}}}"),
|
Macros::parse("{{{poem(red,blue)}}}"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Macros {
|
Macros {
|
||||||
name: "poem".into(),
|
name: "poem".into(),
|
||||||
|
@ -67,8 +64,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_macros::<VerboseError<&str>>("{{{poem())}}}"),
|
Macros::parse("{{{poem())}}}"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Macros {
|
Macros {
|
||||||
name: "poem".into(),
|
name: "poem".into(),
|
||||||
|
@ -77,8 +74,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_macros::<VerboseError<&str>>("{{{author}}}"),
|
Macros::parse("{{{author}}}"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Macros {
|
Macros {
|
||||||
name: "author".into(),
|
name: "author".into(),
|
||||||
|
@ -86,8 +83,9 @@ fn parse() {
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert!(parse_macros::<VerboseError<&str>>("{{{0uthor}}}").is_err());
|
|
||||||
assert!(parse_macros::<VerboseError<&str>>("{{{author}}").is_err());
|
assert!(Macros::parse("{{{0uthor}}}").is_none());
|
||||||
assert!(parse_macros::<VerboseError<&str>>("{{{poem(}}}").is_err());
|
assert!(Macros::parse("{{{author}}").is_none());
|
||||||
assert!(parse_macros::<VerboseError<&str>>("{{{poem)}}}").is_err());
|
assert!(Macros::parse("{{{poem(}}}").is_none());
|
||||||
|
assert!(Macros::parse("{{{poem)}}}").is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take_while},
|
bytes::complete::{tag, take_while},
|
||||||
combinator::verify,
|
combinator::verify,
|
||||||
error::ParseError,
|
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -10,13 +9,11 @@ use nom::{
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse_radio_target(input: &str) -> Option<(&str, &str)> {
|
pub fn parse_radio_target(input: &str) -> Option<(&str, &str)> {
|
||||||
parse_radio_target_internal::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_radio_target_internal<'a, E: ParseError<&'a str>>(
|
fn parse_internal(input: &str) -> IResult<&str, &str, ()> {
|
||||||
input: &'a str,
|
|
||||||
) -> IResult<&str, &str, E> {
|
|
||||||
let (input, contents) = delimited(
|
let (input, contents) = delimited(
|
||||||
tag("<<<"),
|
tag("<<<"),
|
||||||
verify(
|
verify(
|
||||||
|
@ -31,20 +28,13 @@ fn parse_radio_target_internal<'a, E: ParseError<&'a str>>(
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
assert_eq!(parse_radio_target("<<<target>>>"), Some(("", "target")));
|
||||||
|
assert_eq!(parse_radio_target("<<<tar get>>>"), Some(("", "tar get")));
|
||||||
|
|
||||||
assert_eq!(
|
assert!(parse_radio_target("<<<target >>>").is_none());
|
||||||
parse_radio_target_internal::<VerboseError<&str>>("<<<target>>>"),
|
assert!(parse_radio_target("<<< target>>>").is_none());
|
||||||
Ok(("", "target"))
|
assert!(parse_radio_target("<<<ta<get>>>").is_none());
|
||||||
);
|
assert!(parse_radio_target("<<<ta>get>>>").is_none());
|
||||||
assert_eq!(
|
assert!(parse_radio_target("<<<ta\nget>>>").is_none());
|
||||||
parse_radio_target_internal::<VerboseError<&str>>("<<<tar get>>>"),
|
assert!(parse_radio_target("<<<target>>").is_none());
|
||||||
Ok(("", "tar get"))
|
|
||||||
);
|
|
||||||
assert!(parse_radio_target_internal::<VerboseError<&str>>("<<<target >>>").is_err());
|
|
||||||
assert!(parse_radio_target_internal::<VerboseError<&str>>("<<< target>>>").is_err());
|
|
||||||
assert!(parse_radio_target_internal::<VerboseError<&str>>("<<<ta<get>>>").is_err());
|
|
||||||
assert!(parse_radio_target_internal::<VerboseError<&str>>("<<<ta>get>>>").is_err());
|
|
||||||
assert!(parse_radio_target_internal::<VerboseError<&str>>("<<<ta\nget>>>").is_err());
|
|
||||||
assert!(parse_radio_target_internal::<VerboseError<&str>>("<<<target>>").is_err());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use nom::{
|
use nom::{bytes::complete::take_while_m_n, character::complete::space0, IResult};
|
||||||
bytes::complete::take_while_m_n, character::complete::space0, error::ParseError, IResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::parse::combinators::{blank_lines_count, eol};
|
use crate::parse::combinators::{blank_lines_count, eol};
|
||||||
|
|
||||||
|
@ -15,14 +13,11 @@ pub struct Rule {
|
||||||
|
|
||||||
impl Rule {
|
impl Rule {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, Rule)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, Rule)> {
|
||||||
parse_rule::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_rule<'a, E>(input: &'a str) -> IResult<&str, Rule, E>
|
fn parse_internal(input: &str) -> IResult<&str, Rule, ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, _) = space0(input)?;
|
let (input, _) = space0(input)?;
|
||||||
let (input, _) = take_while_m_n(5, usize::max_value(), |c| c == '-')(input)?;
|
let (input, _) = take_while_m_n(5, usize::max_value(), |c| c == '-')(input)?;
|
||||||
let (input, _) = eol(input)?;
|
let (input, _) = eol(input)?;
|
||||||
|
@ -32,32 +27,22 @@ where
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
assert_eq!(Rule::parse("-----"), Some(("", Rule { post_blank: 0 })));
|
||||||
|
assert_eq!(Rule::parse("--------"), Some(("", Rule { post_blank: 0 })));
|
||||||
|
assert_eq!(
|
||||||
|
Rule::parse("-----\n\n\n"),
|
||||||
|
Some(("", Rule { post_blank: 2 }))
|
||||||
|
);
|
||||||
|
assert_eq!(Rule::parse("----- \n"), Some(("", Rule { post_blank: 0 })));
|
||||||
|
|
||||||
assert_eq!(
|
assert!(Rule::parse("").is_none());
|
||||||
parse_rule::<VerboseError<&str>>("-----"),
|
assert!(Rule::parse("----").is_none());
|
||||||
Ok(("", Rule { post_blank: 0 }))
|
assert!(Rule::parse("----").is_none());
|
||||||
);
|
assert!(Rule::parse("None----").is_none());
|
||||||
assert_eq!(
|
assert!(Rule::parse("None ----").is_none());
|
||||||
parse_rule::<VerboseError<&str>>("--------"),
|
assert!(Rule::parse("None------").is_none());
|
||||||
Ok(("", Rule { post_blank: 0 }))
|
assert!(Rule::parse("----None----").is_none());
|
||||||
);
|
assert!(Rule::parse("\t\t----").is_none());
|
||||||
assert_eq!(
|
assert!(Rule::parse("------None").is_none());
|
||||||
parse_rule::<VerboseError<&str>>("-----\n\n\n"),
|
assert!(Rule::parse("----- None").is_none());
|
||||||
Ok(("", Rule { post_blank: 2 }))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
parse_rule::<VerboseError<&str>>("----- \n"),
|
|
||||||
Ok(("", Rule { post_blank: 0 }))
|
|
||||||
);
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("----").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("----").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("None----").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("None ----").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("None------").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("----None----").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("\t\t----").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("------None").is_err());
|
|
||||||
assert!(parse_rule::<VerboseError<&str>>("----- None").is_err());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take, take_until, take_while1},
|
bytes::complete::{tag, take, take_until, take_while1},
|
||||||
error::ParseError,
|
|
||||||
sequence::{delimited, separated_pair},
|
sequence::{delimited, separated_pair},
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -20,7 +19,7 @@ pub struct Snippet<'a> {
|
||||||
|
|
||||||
impl Snippet<'_> {
|
impl Snippet<'_> {
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, Snippet)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, Snippet)> {
|
||||||
parse_snippet::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> Snippet<'static> {
|
pub fn into_owned(self) -> Snippet<'static> {
|
||||||
|
@ -32,7 +31,7 @@ impl Snippet<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_snippet<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Snippet, E> {
|
fn parse_internal(input: &str) -> IResult<&str, Snippet, ()> {
|
||||||
let (input, (name, value)) = delimited(
|
let (input, (name, value)) = delimited(
|
||||||
tag("@@"),
|
tag("@@"),
|
||||||
separated_pair(
|
separated_pair(
|
||||||
|
@ -54,11 +53,9 @@ fn parse_snippet<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Sn
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_snippet::<VerboseError<&str>>("@@html:<b>@@"),
|
Snippet::parse("@@html:<b>@@"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Snippet {
|
Snippet {
|
||||||
name: "html".into(),
|
name: "html".into(),
|
||||||
|
@ -67,8 +64,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_snippet::<VerboseError<&str>>("@@latex:any arbitrary LaTeX code@@"),
|
Snippet::parse("@@latex:any arbitrary LaTeX code@@"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Snippet {
|
Snippet {
|
||||||
name: "latex".into(),
|
name: "latex".into(),
|
||||||
|
@ -77,8 +74,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_snippet::<VerboseError<&str>>("@@html:@@"),
|
Snippet::parse("@@html:@@"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Snippet {
|
Snippet {
|
||||||
name: "html".into(),
|
name: "html".into(),
|
||||||
|
@ -87,8 +84,8 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_snippet::<VerboseError<&str>>("@@html:<p>@</p>@@"),
|
Snippet::parse("@@html:<p>@</p>@@"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Snippet {
|
Snippet {
|
||||||
name: "html".into(),
|
name: "html".into(),
|
||||||
|
@ -96,7 +93,8 @@ fn parse() {
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert!(parse_snippet::<VerboseError<&str>>("@@html:<b>@").is_err());
|
|
||||||
assert!(parse_snippet::<VerboseError<&str>>("@@html<b>@@").is_err());
|
assert!(Snippet::parse("@@html:<b>@").is_none());
|
||||||
assert!(parse_snippet::<VerboseError<&str>>("@@:<b>@@").is_err());
|
assert!(Snippet::parse("@@html<b>@@").is_none());
|
||||||
|
assert!(Snippet::parse("@@:<b>@@").is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
error::{ErrorKind, ParseError},
|
error::{make_error, ErrorKind},
|
||||||
Err, IResult,
|
Err, IResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,13 +35,10 @@ pub enum Table<'a> {
|
||||||
|
|
||||||
impl Table<'_> {
|
impl Table<'_> {
|
||||||
pub fn parse_table_el(input: &str) -> Option<(&str, Table)> {
|
pub fn parse_table_el(input: &str) -> Option<(&str, Table)> {
|
||||||
Self::parse_table_el_internal::<()>(input).ok()
|
Self::parse_table_el_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_table_el_internal<'a, E>(input: &'a str) -> IResult<&str, Table, E>
|
fn parse_table_el_internal(input: &str) -> IResult<&str, Table, ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (_, first_line) = line(input)?;
|
let (_, first_line) = line(input)?;
|
||||||
|
|
||||||
let first_line = first_line.trim();
|
let first_line = first_line.trim();
|
||||||
|
@ -54,7 +51,7 @@ impl Table<'_> {
|
||||||
.any(|&c| c != b'+' && c != b'-')
|
.any(|&c| c != b'+' && c != b'-')
|
||||||
{
|
{
|
||||||
// TODO: better error kind
|
// TODO: better error kind
|
||||||
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Many0)));
|
return Err(Err::Error(make_error(input, ErrorKind::Many0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Table.el tables end at the first line not starting with either a vertical line or a plus sign.
|
// Table.el tables end at the first line not starting with either a vertical line or a plus sign.
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::{tag, take_while},
|
bytes::complete::{tag, take_while},
|
||||||
combinator::verify,
|
combinator::verify,
|
||||||
error::ParseError,
|
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -20,7 +19,7 @@ pub struct Target<'a> {
|
||||||
impl Target<'_> {
|
impl Target<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn parse(input: &str) -> Option<(&str, Target)> {
|
pub(crate) fn parse(input: &str) -> Option<(&str, Target)> {
|
||||||
parse_target::<()>(input).ok()
|
parse_internal(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> Target<'static> {
|
pub fn into_owned(self) -> Target<'static> {
|
||||||
|
@ -31,7 +30,7 @@ impl Target<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_target<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Target, E> {
|
fn parse_internal(input: &str) -> IResult<&str, Target, ()> {
|
||||||
let (input, target) = delimited(
|
let (input, target) = delimited(
|
||||||
tag("<<"),
|
tag("<<"),
|
||||||
verify(
|
verify(
|
||||||
|
@ -51,11 +50,9 @@ fn parse_target<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Tar
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_target::<VerboseError<&str>>("<<target>>"),
|
Target::parse("<<target>>"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Target {
|
Target {
|
||||||
target: "target".into()
|
target: "target".into()
|
||||||
|
@ -63,18 +60,19 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_target::<VerboseError<&str>>("<<tar get>>"),
|
Target::parse("<<tar get>>"),
|
||||||
Ok((
|
Some((
|
||||||
"",
|
"",
|
||||||
Target {
|
Target {
|
||||||
target: "tar get".into()
|
target: "tar get".into()
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert!(parse_target::<VerboseError<&str>>("<<target >>").is_err());
|
|
||||||
assert!(parse_target::<VerboseError<&str>>("<< target>>").is_err());
|
assert!(Target::parse("<<target >>").is_none());
|
||||||
assert!(parse_target::<VerboseError<&str>>("<<ta<get>>").is_err());
|
assert!(Target::parse("<< target>>").is_none());
|
||||||
assert!(parse_target::<VerboseError<&str>>("<<ta>get>>").is_err());
|
assert!(Target::parse("<<ta<get>>").is_none());
|
||||||
assert!(parse_target::<VerboseError<&str>>("<<ta\nget>>").is_err());
|
assert!(Target::parse("<<ta>get>>").is_none());
|
||||||
assert!(parse_target::<VerboseError<&str>>("<<target>").is_err());
|
assert!(Target::parse("<<ta\nget>>").is_none());
|
||||||
|
assert!(Target::parse("<<target>").is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ use nom::{
|
||||||
bytes::complete::{tag, take, take_till, take_while, take_while_m_n},
|
bytes::complete::{tag, take, take_till, take_while, take_while_m_n},
|
||||||
character::complete::{space0, space1},
|
character::complete::{space0, space1},
|
||||||
combinator::{map, map_res, opt},
|
combinator::{map, map_res, opt},
|
||||||
error::ParseError,
|
|
||||||
sequence::preceded,
|
sequence::preceded,
|
||||||
IResult,
|
IResult,
|
||||||
};
|
};
|
||||||
|
@ -139,15 +138,15 @@ pub enum Timestamp<'a> {
|
||||||
|
|
||||||
impl Timestamp<'_> {
|
impl Timestamp<'_> {
|
||||||
pub(crate) fn parse_active(input: &str) -> Option<(&str, Timestamp)> {
|
pub(crate) fn parse_active(input: &str) -> Option<(&str, Timestamp)> {
|
||||||
parse_active::<()>(input).ok()
|
parse_active(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_inactive(input: &str) -> Option<(&str, Timestamp)> {
|
pub(crate) fn parse_inactive(input: &str) -> Option<(&str, Timestamp)> {
|
||||||
parse_inactive::<()>(input).ok()
|
parse_inactive(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_diary(input: &str) -> Option<(&str, Timestamp)> {
|
pub(crate) fn parse_diary(input: &str) -> Option<(&str, Timestamp)> {
|
||||||
parse_diary::<()>(input).ok()
|
parse_diary(input).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_owned(self) -> Timestamp<'static> {
|
pub fn into_owned(self) -> Timestamp<'static> {
|
||||||
|
@ -199,7 +198,7 @@ impl Timestamp<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_active<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Timestamp, E> {
|
pub fn parse_active(input: &str) -> IResult<&str, Timestamp, ()> {
|
||||||
let (input, _) = tag("<")(input)?;
|
let (input, _) = tag("<")(input)?;
|
||||||
let (input, start) = parse_datetime(input)?;
|
let (input, start) = parse_datetime(input)?;
|
||||||
|
|
||||||
|
@ -252,7 +251,7 @@ pub fn parse_active<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_inactive<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Timestamp, E> {
|
pub fn parse_inactive(input: &str) -> IResult<&str, Timestamp, ()> {
|
||||||
let (input, _) = tag("[")(input)?;
|
let (input, _) = tag("[")(input)?;
|
||||||
let (input, start) = parse_datetime(input)?;
|
let (input, start) = parse_datetime(input)?;
|
||||||
|
|
||||||
|
@ -305,7 +304,7 @@ pub fn parse_inactive<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_diary<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Timestamp, E> {
|
pub fn parse_diary(input: &str) -> IResult<&str, Timestamp, ()> {
|
||||||
let (input, _) = tag("<%%(")(input)?;
|
let (input, _) = tag("<%%(")(input)?;
|
||||||
let (input, value) = take_till(|c| c == ')' || c == '>' || c == '\n')(input)?;
|
let (input, value) = take_till(|c| c == ')' || c == '>' || c == '\n')(input)?;
|
||||||
let (input, _) = tag(")>")(input)?;
|
let (input, _) = tag(")>")(input)?;
|
||||||
|
@ -318,7 +317,7 @@ pub fn parse_diary<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_time<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, (u8, u8), E> {
|
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| {
|
let (input, hour) = map_res(take_while_m_n(1, 2, |c: char| c.is_ascii_digit()), |num| {
|
||||||
u8::from_str_radix(num, 10)
|
u8::from_str_radix(num, 10)
|
||||||
})(input)?;
|
})(input)?;
|
||||||
|
@ -327,7 +326,7 @@ fn parse_time<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, (u8,
|
||||||
Ok((input, (hour, minute)))
|
Ok((input, (hour, minute)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_datetime<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Datetime, E> {
|
fn parse_datetime(input: &str) -> IResult<&str, Datetime, ()> {
|
||||||
let parse_u8 = |num| u8::from_str_radix(num, 10);
|
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, year) = map_res(take(4usize), |num| u16::from_str_radix(num, 10))(input)?;
|
||||||
|
@ -410,10 +409,8 @@ fn parse_datetime<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, D
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inactive::<VerboseError<&str>>("[2003-09-16 Tue]"),
|
parse_inactive("[2003-09-16 Tue]"),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
Timestamp::Inactive {
|
Timestamp::Inactive {
|
||||||
|
@ -431,7 +428,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_inactive::<VerboseError<&str>>("[2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39]"),
|
parse_inactive("[2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39]"),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
Timestamp::InactiveRange {
|
Timestamp::InactiveRange {
|
||||||
|
@ -457,7 +454,7 @@ fn parse() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_active::<VerboseError<&str>>("<2003-09-16 Tue 09:39-10:39>"),
|
parse_active("<2003-09-16 Tue 09:39-10:39>"),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
Timestamp::ActiveRange {
|
Timestamp::ActiveRange {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use nom::{
|
||||||
bytes::complete::{tag, take_until, take_while},
|
bytes::complete::{tag, take_until, take_while},
|
||||||
character::complete::{anychar, space1},
|
character::complete::{anychar, space1},
|
||||||
combinator::{map, map_parser, opt, verify},
|
combinator::{map, map_parser, opt, verify},
|
||||||
error::{ErrorKind, ParseError},
|
error::{make_error, ErrorKind},
|
||||||
multi::fold_many0,
|
multi::fold_many0,
|
||||||
sequence::{delimited, preceded},
|
sequence::{delimited, preceded},
|
||||||
Err, IResult,
|
Err, IResult,
|
||||||
|
@ -54,7 +54,7 @@ impl Title<'_> {
|
||||||
input: &'a str,
|
input: &'a str,
|
||||||
config: &ParseConfig,
|
config: &ParseConfig,
|
||||||
) -> Option<(&'a str, (Title<'a>, &'a str))> {
|
) -> Option<(&'a str, (Title<'a>, &'a str))> {
|
||||||
parse_title::<()>(input, config).ok()
|
parse_title(input, config).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fn is_quoted(&self) -> bool { }
|
// TODO: fn is_quoted(&self) -> bool { }
|
||||||
|
@ -123,13 +123,7 @@ impl Default for Title<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_title<'a, E>(
|
fn parse_title<'a>(input: &'a str, config: &ParseConfig) -> IResult<&'a str, (Title<'a>, &'a str), ()> {
|
||||||
input: &'a str,
|
|
||||||
config: &ParseConfig,
|
|
||||||
) -> IResult<&'a str, (Title<'a>, &'a str), E>
|
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let (input, level) = map(take_while(|c: char| c == '*'), |s: &str| s.len())(input)?;
|
let (input, level) = map(take_while(|c: char| c == '*'), |s: &str| s.len())(input)?;
|
||||||
|
|
||||||
debug_assert!(level > 0);
|
debug_assert!(level > 0);
|
||||||
|
@ -203,12 +197,10 @@ fn is_tag_line(input: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_properties_drawer<'a, E: ParseError<&'a str>>(
|
fn parse_properties_drawer(input: &str) -> IResult<&str, HashMap<Cow<'_, str>, Cow<'_, str>>, ()> {
|
||||||
input: &'a str,
|
|
||||||
) -> IResult<&str, HashMap<Cow<'_, str>, Cow<'_, str>>, E> {
|
|
||||||
let (input, (drawer, content)) = parse_drawer_without_blank(input.trim_start())?;
|
let (input, (drawer, content)) = parse_drawer_without_blank(input.trim_start())?;
|
||||||
if drawer.name != "PROPERTIES" {
|
if drawer.name != "PROPERTIES" {
|
||||||
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Tag)));
|
return Err(Err::Error(make_error(input, ErrorKind::Tag)));
|
||||||
}
|
}
|
||||||
let (_, map) = fold_many0(
|
let (_, map) = fold_many0(
|
||||||
parse_node_property,
|
parse_node_property,
|
||||||
|
@ -222,9 +214,7 @@ fn parse_properties_drawer<'a, E: ParseError<&'a str>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_node_property<'a, E: ParseError<&'a str>>(
|
fn parse_node_property(input: &str) -> IResult<&str, (&str, &str), ()> {
|
||||||
input: &'a str,
|
|
||||||
) -> IResult<&str, (&str, &str), E> {
|
|
||||||
let (input, _) = blank_lines_count(input)?;
|
let (input, _) = blank_lines_count(input)?;
|
||||||
let input = input.trim_start();
|
let input = input.trim_start();
|
||||||
let (input, name) = map(delimited(tag(":"), take_until(":"), tag(":")), |s: &str| {
|
let (input, name) = map(delimited(tag(":"), take_until(":"), tag(":")), |s: &str| {
|
||||||
|
@ -236,15 +226,10 @@ fn parse_node_property<'a, E: ParseError<&'a str>>(
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_title_() {
|
fn parse_title_() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
use crate::config::DEFAULT_CONFIG;
|
use crate::config::DEFAULT_CONFIG;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>(
|
parse_title("**** DONE [#A] COMMENT Title :tag:a2%:", &DEFAULT_CONFIG),
|
||||||
"**** DONE [#A] COMMENT Title :tag:a2%:",
|
|
||||||
&DEFAULT_CONFIG
|
|
||||||
),
|
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
|
@ -263,7 +248,7 @@ fn parse_title_() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>("**** ToDO [#A] COMMENT Title", &DEFAULT_CONFIG),
|
parse_title("**** ToDO [#A] COMMENT Title", &DEFAULT_CONFIG),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
|
@ -282,7 +267,7 @@ fn parse_title_() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>("**** T0DO [#A] COMMENT Title", &DEFAULT_CONFIG),
|
parse_title("**** T0DO [#A] COMMENT Title", &DEFAULT_CONFIG),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
|
@ -301,7 +286,7 @@ fn parse_title_() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>("**** DONE [#1] COMMENT Title", &DEFAULT_CONFIG),
|
parse_title("**** DONE [#1] COMMENT Title", &DEFAULT_CONFIG),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
|
@ -320,7 +305,7 @@ fn parse_title_() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>("**** DONE [#a] COMMENT Title", &DEFAULT_CONFIG),
|
parse_title("**** DONE [#a] COMMENT Title", &DEFAULT_CONFIG),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
|
@ -339,7 +324,7 @@ fn parse_title_() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>("**** Title :tag:a2%", &DEFAULT_CONFIG),
|
parse_title("**** Title :tag:a2%", &DEFAULT_CONFIG),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
|
@ -358,7 +343,7 @@ fn parse_title_() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>("**** Title tag:a2%:", &DEFAULT_CONFIG),
|
parse_title("**** Title tag:a2%:", &DEFAULT_CONFIG),
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
(
|
(
|
||||||
|
@ -378,7 +363,7 @@ fn parse_title_() {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>(
|
parse_title(
|
||||||
"**** DONE Title",
|
"**** DONE Title",
|
||||||
&ParseConfig {
|
&ParseConfig {
|
||||||
todo_keywords: (vec![], vec![]),
|
todo_keywords: (vec![], vec![]),
|
||||||
|
@ -403,7 +388,7 @@ fn parse_title_() {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_title::<VerboseError<&str>>(
|
parse_title(
|
||||||
"**** TASK [#A] Title",
|
"**** TASK [#A] Title",
|
||||||
&ParseConfig {
|
&ParseConfig {
|
||||||
todo_keywords: (vec!["TASK".to_string()], vec![]),
|
todo_keywords: (vec!["TASK".to_string()], vec![]),
|
||||||
|
@ -431,12 +416,8 @@ fn parse_title_() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_properties_drawer_() {
|
fn parse_properties_drawer_() {
|
||||||
use nom::error::VerboseError;
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_properties_drawer::<VerboseError<&str>>(
|
parse_properties_drawer(" :PROPERTIES:\n :CUSTOM_ID: id\n :END:"),
|
||||||
" :PROPERTIES:\n :CUSTOM_ID: id\n :END:"
|
|
||||||
),
|
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
vec![("CUSTOM_ID".into(), "id".into())]
|
vec![("CUSTOM_ID".into(), "id".into())]
|
||||||
|
|
|
@ -4,15 +4,12 @@ use memchr::memchr;
|
||||||
use nom::{
|
use nom::{
|
||||||
bytes::complete::take_while1,
|
bytes::complete::take_while1,
|
||||||
combinator::verify,
|
combinator::verify,
|
||||||
error::{ErrorKind, ParseError},
|
error::{make_error, ErrorKind},
|
||||||
Err, IResult,
|
Err, IResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
// read until the first line_ending, if line_ending is not present, return the input directly
|
// read until the first line_ending, if line_ending is not present, return the input directly
|
||||||
pub fn line<'a, E>(input: &'a str) -> IResult<&'a str, &'a str, E>
|
pub fn line(input: &str) -> IResult<&str, &str, ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
if let Some(i) = memchr(b'\n', input.as_bytes()) {
|
if let Some(i) = memchr(b'\n', input.as_bytes()) {
|
||||||
if i > 0 && input.as_bytes()[i - 1] == b'\r' {
|
if i > 0 && input.as_bytes()[i - 1] == b'\r' {
|
||||||
Ok((&input[i + 1..], &input[0..i - 1]))
|
Ok((&input[i + 1..], &input[0..i - 1]))
|
||||||
|
@ -24,10 +21,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lines_till<'a, F, E>(predicate: F) -> impl Fn(&'a str) -> IResult<&str, &str, E>
|
pub fn lines_till<F>(predicate: F) -> impl Fn(&str) -> IResult<&str, &str, ()>
|
||||||
where
|
where
|
||||||
F: Fn(&str) -> bool,
|
F: Fn(&str) -> bool,
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
{
|
||||||
move |i| {
|
move |i| {
|
||||||
let mut input = i;
|
let mut input = i;
|
||||||
|
@ -35,7 +31,7 @@ where
|
||||||
loop {
|
loop {
|
||||||
// TODO: better error kind
|
// TODO: better error kind
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Many0)));
|
return Err(Err::Error(make_error(input, ErrorKind::Many0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (input_, line_) = line(input)?;
|
let (input_, line_) = line(input)?;
|
||||||
|
@ -52,10 +48,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lines_while<'a, F, E>(predicate: F) -> impl Fn(&'a str) -> IResult<&str, &str, E>
|
pub fn lines_while<F>(predicate: F) -> impl Fn(&str) -> IResult<&str, &str, ()>
|
||||||
where
|
where
|
||||||
F: Fn(&str) -> bool,
|
F: Fn(&str) -> bool,
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
{
|
||||||
move |i| {
|
move |i| {
|
||||||
let mut input = i;
|
let mut input = i;
|
||||||
|
@ -82,44 +77,29 @@ where
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lines_while() {
|
fn test_lines_while() {
|
||||||
|
assert_eq!(lines_while(|line| line == "foo")("foo"), Ok(("", "foo")));
|
||||||
|
assert_eq!(lines_while(|line| line == "foo")("bar"), Ok(("bar", "")));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lines_while::<_, ()>(|line| line == "foo")("foo"),
|
lines_while(|line| line == "foo")("foo\n\n"),
|
||||||
Ok(("", "foo"))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
lines_while::<_, ()>(|line| line == "foo")("bar"),
|
|
||||||
Ok(("bar", ""))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
lines_while::<_, ()>(|line| line == "foo")("foo\n\n"),
|
|
||||||
Ok(("\n", "foo\n"))
|
Ok(("\n", "foo\n"))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lines_while::<_, ()>(|line| line.trim().is_empty())("\n\n\n"),
|
lines_while(|line| line.trim().is_empty())("\n\n\n"),
|
||||||
Ok(("", "\n\n\n"))
|
Ok(("", "\n\n\n"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eol<'a, E>(input: &'a str) -> IResult<&str, &str, E>
|
pub fn eol(input: &str) -> IResult<&str, &str, ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
verify(line, |s: &str| {
|
verify(line, |s: &str| {
|
||||||
s.as_bytes().iter().all(u8::is_ascii_whitespace)
|
s.as_bytes().iter().all(u8::is_ascii_whitespace)
|
||||||
})(input)
|
})(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn one_word<'a, E>(input: &'a str) -> IResult<&str, &str, E>
|
pub fn one_word(input: &str) -> IResult<&str, &str, ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
take_while1(|c: char| !c.is_ascii_whitespace())(input)
|
take_while1(|c: char| !c.is_ascii_whitespace())(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blank_lines_count<'a, E>(input: &'a str) -> IResult<&str, usize, E>
|
pub fn blank_lines_count(input: &str) -> IResult<&str, usize, ()> {
|
||||||
where
|
|
||||||
E: ParseError<&'a str>,
|
|
||||||
{
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut input = input;
|
let mut input = input;
|
||||||
|
|
||||||
|
@ -144,16 +124,13 @@ where
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_blank_lines_count() {
|
fn test_blank_lines_count() {
|
||||||
assert_eq!(blank_lines_count::<()>("foo"), Ok(("foo", 0)));
|
assert_eq!(blank_lines_count("foo"), Ok(("foo", 0)));
|
||||||
assert_eq!(blank_lines_count::<()>(" foo"), Ok((" foo", 0)));
|
assert_eq!(blank_lines_count(" foo"), Ok((" foo", 0)));
|
||||||
assert_eq!(blank_lines_count::<()>(" \t\nfoo\n"), Ok(("foo\n", 1)));
|
assert_eq!(blank_lines_count(" \t\nfoo\n"), Ok(("foo\n", 1)));
|
||||||
|
assert_eq!(blank_lines_count("\n \r\n\nfoo\n"), Ok(("foo\n", 3)));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
blank_lines_count::<()>("\n \r\n\nfoo\n"),
|
blank_lines_count("\r\n \n \r\n foo\n"),
|
||||||
Ok(("foo\n", 3))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
blank_lines_count::<()>("\r\n \n \r\n foo\n"),
|
|
||||||
Ok((" foo\n", 3))
|
Ok((" foo\n", 3))
|
||||||
);
|
);
|
||||||
assert_eq!(blank_lines_count::<()>("\r\n \n \r\n \n"), Ok(("", 4)));
|
assert_eq!(blank_lines_count("\r\n \n \r\n \n"), Ok(("", 4)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -560,8 +560,7 @@ pub fn parse_org_table<'a, T: ElementArena<'a>>(
|
||||||
parent: NodeId,
|
parent: NodeId,
|
||||||
) -> &'a str {
|
) -> &'a str {
|
||||||
let (tail, contents) =
|
let (tail, contents) =
|
||||||
lines_while::<_, ()>(|line| line.trim_start().starts_with('|'))(contents)
|
lines_while(|line| line.trim_start().starts_with('|'))(contents).unwrap_or((contents, ""));
|
||||||
.unwrap_or((contents, ""));
|
|
||||||
let (tail, post_blank) = blank_lines_count(tail);
|
let (tail, post_blank) = blank_lines_count(tail);
|
||||||
|
|
||||||
let mut iter = contents.trim_end().lines().peekable();
|
let mut iter = contents.trim_end().lines().peekable();
|
||||||
|
@ -633,12 +632,12 @@ pub fn parse_org_table<'a, T: ElementArena<'a>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blank_lines_count(input: &str) -> (&str, usize) {
|
pub fn blank_lines_count(input: &str) -> (&str, usize) {
|
||||||
crate::parse::combinators::blank_lines_count::<()>(input).unwrap_or((input, 0))
|
crate::parse::combinators::blank_lines_count(input).unwrap_or((input, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_headline(input: &str) -> Option<(&str, (&str, usize))> {
|
pub fn parse_headline(input: &str) -> Option<(&str, (&str, usize))> {
|
||||||
let (input_, level) = parse_headline_level(input)?;
|
let (input_, level) = parse_headline_level(input)?;
|
||||||
let (input_, content) = lines_while::<_, ()>(move |line| {
|
let (input_, content) = lines_while(move |line| {
|
||||||
parse_headline_level(line)
|
parse_headline_level(line)
|
||||||
.map(|(_, l)| l > level)
|
.map(|(_, l)| l > level)
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
|
|
Loading…
Reference in a new issue