orgize/parse/
combinators.rs1use memchr::memchr;
4use nom::{
5 bytes::complete::take_while1,
6 combinator::verify,
7 error::{make_error, ErrorKind},
8 Err, IResult,
9};
10
11pub fn line(input: &str) -> IResult<&str, &str, ()> {
13 if let Some(i) = memchr(b'\n', input.as_bytes()) {
14 if i > 0 && input.as_bytes()[i - 1] == b'\r' {
15 Ok((&input[i + 1..], &input[0..i - 1]))
16 } else {
17 Ok((&input[i + 1..], &input[0..i]))
18 }
19 } else {
20 Ok(("", input))
21 }
22}
23
24pub fn lines_till<F>(predicate: F) -> impl Fn(&str) -> IResult<&str, &str, ()>
25where
26 F: Fn(&str) -> bool,
27{
28 move |i| {
29 let mut input = i;
30
31 loop {
32 if input.is_empty() {
34 return Err(Err::Error(make_error(input, ErrorKind::Many0)));
35 }
36
37 let (input_, line_) = line(input)?;
38
39 debug_assert_ne!(input, input_);
40
41 if predicate(line_) {
42 let offset = i.len() - input.len();
43 return Ok((input_, &i[0..offset]));
44 }
45
46 input = input_;
47 }
48 }
49}
50
51pub fn lines_while<F>(predicate: F) -> impl Fn(&str) -> IResult<&str, &str, ()>
52where
53 F: Fn(&str) -> bool,
54{
55 move |i| {
56 let mut input = i;
57
58 loop {
59 if input.is_empty() {
61 return Ok(("", i));
62 }
63
64 let (input_, line_) = line(input)?;
65
66 debug_assert_ne!(input, input_);
67
68 if !predicate(line_) {
69 let offset = i.len() - input.len();
70 return Ok((input, &i[0..offset]));
71 }
72
73 input = input_;
74 }
75 }
76}
77
78#[test]
79fn test_lines_while() {
80 assert_eq!(lines_while(|line| line == "foo")("foo"), Ok(("", "foo")));
81 assert_eq!(lines_while(|line| line == "foo")("bar"), Ok(("bar", "")));
82 assert_eq!(
83 lines_while(|line| line == "foo")("foo\n\n"),
84 Ok(("\n", "foo\n"))
85 );
86 assert_eq!(
87 lines_while(|line| line.trim().is_empty())("\n\n\n"),
88 Ok(("", "\n\n\n"))
89 );
90}
91
92pub fn eol(input: &str) -> IResult<&str, &str, ()> {
93 verify(line, |s: &str| {
94 s.as_bytes().iter().all(u8::is_ascii_whitespace)
95 })(input)
96}
97
98pub fn one_word(input: &str) -> IResult<&str, &str, ()> {
99 take_while1(|c: char| !c.is_ascii_whitespace())(input)
100}
101
102pub fn blank_lines_count(input: &str) -> IResult<&str, usize, ()> {
103 let mut count = 0;
104 let mut input = input;
105
106 loop {
107 if input.is_empty() {
108 return Ok(("", count));
109 }
110
111 let (input_, line_) = line(input)?;
112
113 debug_assert_ne!(input, input_);
114
115 if !line_.chars().all(char::is_whitespace) {
116 return Ok((input, count));
117 }
118
119 count += 1;
120
121 input = input_;
122 }
123}
124
125#[test]
126fn test_blank_lines_count() {
127 assert_eq!(blank_lines_count("foo"), Ok(("foo", 0)));
128 assert_eq!(blank_lines_count(" foo"), Ok((" foo", 0)));
129 assert_eq!(blank_lines_count(" \t\nfoo\n"), Ok(("foo\n", 1)));
130 assert_eq!(blank_lines_count("\n \r\n\nfoo\n"), Ok(("foo\n", 3)));
131 assert_eq!(
132 blank_lines_count("\r\n \n \r\n foo\n"),
133 Ok((" foo\n", 3))
134 );
135 assert_eq!(blank_lines_count("\r\n \n \r\n \n"), Ok(("", 4)));
136}