plantuml_parser/dsl/line/
empty.rs

1use crate::{ParseContainer, ParseResult, wr};
2use nom::branch::alt;
3use nom::bytes::complete::{tag, take_till, take_until};
4use nom::character::complete::{line_ending, space0};
5use nom::combinator::{eof, map};
6use nom::multi::many_till;
7use nom::{IResult, Parser};
8
9/// A token sequence that is an empty line. (like `" \t  \n"`, `"' comment\n"`, `" /' oneline block comment '/ "`.)
10///
11/// * `\n`
12/// * `  \n`
13/// * `\t  \n`
14/// * `' comment\n`
15/// * " /' oneline block comment '/ "
16///
17/// # Examples
18///
19/// ```
20/// use plantuml_parser::{EmptyLine, ParseContainer};
21///
22/// # fn main() -> anyhow::Result<()> {
23/// let input = "  \n";
24/// let (rest, (raws, _token)) = EmptyLine::parse(input.into())?;
25/// let combined_raw: ParseContainer = raws.into();
26/// assert_eq!(rest, "");
27/// assert_eq!(combined_raw, "  \n");
28///
29/// let input = "  ' comment \n";
30/// let (rest, (raws, _token)) = EmptyLine::parse(input.into())?;
31/// let combined_raw: ParseContainer = raws.into();
32/// assert_eq!(rest, "");
33/// assert_eq!(combined_raw, "  ' comment \n");
34/// # Ok(())
35/// # }
36/// ```
37#[derive(Clone, Debug)]
38pub struct EmptyLine;
39
40impl EmptyLine {
41    /// Tries to parse [`EmptyLine`]. (e.g. `"   \n"`, `"   ' comment \n"`, `" /' oneline block comment '/ "`.)
42    pub fn parse(input: ParseContainer) -> ParseResult<Self> {
43        let (rest, parsed) =
44            alt((parse_only_space, parse_comment, parse_oneline_block_comment)).parse(input)?;
45
46        let ret0 = ParseContainer::from(parsed);
47        let ret1 = Self;
48
49        Ok((rest, (ret0, ret1)))
50    }
51}
52
53fn parse_only_space(input: ParseContainer) -> IResult<ParseContainer, Vec<ParseContainer>> {
54    let (rest, parsed) = (wr!(space0), alt((wr!(eof), wr!(line_ending)))).parse(input)?;
55
56    let parsed = Vec::from(<[ParseContainer; 2]>::from(parsed));
57    Ok((rest, parsed))
58}
59
60fn parse_comment(input: ParseContainer) -> IResult<ParseContainer, Vec<ParseContainer>> {
61    let (rest, parsed) = (
62        wr!(space0),
63        wr!(tag("'")),
64        wr!(take_till(|c| c == '\n' || c == '\r')),
65        alt((wr!(eof), wr!(line_ending))),
66    )
67        .parse(input)?;
68
69    let parsed = Vec::from(<[ParseContainer; 4]>::from(parsed));
70    Ok((rest, parsed))
71}
72
73fn parse_oneline_block_comment(
74    input: ParseContainer,
75) -> IResult<ParseContainer, Vec<ParseContainer>> {
76    let (rest, parsed) = (
77        wr!(space0),
78        wr!(tag("/'")),
79        map(
80            many_till(
81                (wr!(take_until("'/")), wr!(tag("'/")), wr!(space0)),
82                alt((wr!(eof), wr!(line_ending))),
83            ),
84            |(many_closes, ending)| {
85                let mut flattened: Vec<_> = many_closes
86                    .into_iter()
87                    .flat_map(|(comment, close_tag, space0_1)| [comment, close_tag, space0_1])
88                    .collect();
89                flattened.push(ending);
90                flattened
91            },
92        ),
93    )
94        .parse(input)?;
95
96    let (space0_0, open_tag, flattened) = parsed;
97
98    let mut parsed = Vec::from(<[ParseContainer; 2]>::from((space0_0, open_tag)));
99    parsed.extend(flattened);
100
101    Ok((rest, parsed))
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn test_parse_only_space() -> anyhow::Result<()> {
110        let testdata = "\n";
111        let (rest, parsed) = parse_only_space(testdata.into())?;
112        assert_eq!(rest, "");
113        assert_eq!(testdata, ParseContainer::from(parsed).as_str());
114
115        let testdata = "  \n";
116        let (rest, parsed) = parse_only_space(testdata.into())?;
117        assert_eq!(rest, "");
118        assert_eq!(testdata, ParseContainer::from(parsed).as_str());
119
120        Ok(())
121    }
122
123    #[test]
124    fn test_parse_comment() -> anyhow::Result<()> {
125        let testdata = "' comment\n";
126        let (rest, parsed) = parse_comment(testdata.into())?;
127        assert_eq!(rest, "");
128        assert_eq!(testdata, ParseContainer::from(parsed).as_str());
129
130        let testdata = "  ' comment \n";
131        let (rest, parsed) = parse_comment(testdata.into())?;
132        assert_eq!(rest, "");
133        assert_eq!(testdata, ParseContainer::from(parsed).as_str());
134
135        Ok(())
136    }
137
138    #[test]
139    fn test_parse_oneline_block_comment() -> anyhow::Result<()> {
140        let testdata = [
141            " /' oneline block comment '/ \n",
142            "  /' oneline block comment '/   \n",
143            "/' oneline /' block '/ comment '/\n",
144            "  /' oneline '/ /' block '/ /' comment '/   \n",
145        ];
146
147        for testdata in testdata.into_iter() {
148            let (rest, parsed) = parse_oneline_block_comment(testdata.into())?;
149            assert_eq!(rest, "");
150            assert_eq!(testdata, ParseContainer::from(parsed).as_str());
151        }
152
153        let testdata = "  /' oneline '/ /' block '/ /' comment '/   \n/' line 2 '/\n";
154        let (rest, parsed) = parse_oneline_block_comment(testdata.into())?;
155        assert_eq!(rest, "/' line 2 '/\n");
156        assert_eq!(
157            &testdata[0..testdata.len() - rest.len()],
158            ParseContainer::from(parsed).as_str()
159        );
160
161        let testdata = rest.as_str();
162        let (rest, parsed) = parse_oneline_block_comment(testdata.into())?;
163        assert_eq!(rest, "");
164        assert_eq!(testdata, ParseContainer::from(parsed).as_str());
165
166        Ok(())
167    }
168
169    #[test]
170    fn test_parse() -> anyhow::Result<()> {
171        let testdata = [
172            "\n",
173            "  \n",
174            "' comment\n",
175            "  ' comment \n",
176            "  /' oneline block comment '/   \n",
177        ];
178
179        for testdata in testdata.into_iter() {
180            println!("try: testdata = {testdata:?}");
181            let (rest, (parsed, _)) = EmptyLine::parse(testdata.into())?;
182            assert_eq!(rest, "");
183            assert_eq!(testdata, ParseContainer::from(parsed).as_str());
184        }
185
186        Ok(())
187    }
188}