1use crate::literal::DefaultValue;
2use crate::{term, IResult, Parse};
3
4pub(crate) fn is_alphanum_underscore_dash(token: char) -> bool {
5    nom::AsChar::is_alphanum(token) || matches!(token, '_' | '-')
6}
7
8fn marker<S>(i: &str) -> IResult<&str, S>
9where
10    S: ::std::default::Default,
11{
12    Ok((i, S::default()))
13}
14
15impl<'a, T: Parse<'a>> Parse<'a> for Option<T> {
16    parser!(nom::combinator::opt(weedle!(T)));
17}
18
19impl<'a, T: Parse<'a>> Parse<'a> for Box<T> {
20    parser!(nom::combinator::map(weedle!(T), Box::new));
21}
22
23impl<'a, T: Parse<'a>> Parse<'a> for Vec<T> {
25    parser!(nom::multi::many0(T::parse));
26}
27
28impl<'a, T: Parse<'a>, U: Parse<'a>> Parse<'a> for (T, U) {
29    parser!(nom::sequence::tuple((T::parse, U::parse)));
30}
31
32impl<'a, T: Parse<'a>, U: Parse<'a>, V: Parse<'a>> Parse<'a> for (T, U, V) {
33    parser!(nom::sequence::tuple((T::parse, U::parse, V::parse)));
34}
35
36pub(crate) fn docstring(input: &str) -> IResult<&str, String> {
37    nom::multi::many1(nom::sequence::preceded(
38        nom::character::complete::multispace0,
39        nom::sequence::delimited(
40            nom::bytes::complete::tag("///"),
41            nom::bytes::complete::take_until("\n"),
42            nom::bytes::complete::tag("\n"),
43        ),
44    ))(input)
45    .map(|io| (io.0, io.1.join("\n")))
46}
47
48ast_types! {
49    #[derive(Copy, Default)]
51    struct Parenthesized<T> where [T: Parse<'a>] {
52        open_paren: term::OpenParen,
53        body: T,
54        close_paren: term::CloseParen,
55    }
56
57    #[derive(Copy, Default)]
59    struct Bracketed<T> where [T: Parse<'a>] {
60        open_bracket: term::OpenBracket,
61        body: T,
62        close_bracket: term::CloseBracket,
63    }
64
65    #[derive(Copy, Default)]
67    struct Braced<T> where [T: Parse<'a>] {
68        open_brace: term::OpenBrace,
69        body: T,
70        close_brace: term::CloseBrace,
71    }
72
73    #[derive(Copy, Default)]
75    struct Generics<T> where [T: Parse<'a>] {
76        open_angle: term::LessThan,
77        body: T,
78        close_angle: term::GreaterThan,
79    }
80
81    struct Punctuated<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] {
83        list: Vec<T> = nom::multi::separated_list0(weedle!(S), weedle!(T)),
84        separator: S = marker,
85    }
86
87    struct PunctuatedNonEmpty<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] {
89        list: Vec<T> = nom::sequence::terminated(
90            nom::multi::separated_list1(weedle!(S), weedle!(T)),
91            nom::combinator::opt(weedle!(S))
92        ),
93        separator: S = marker,
94    }
95
96    #[derive(Copy)]
100    struct Identifier<'a>(
101        &'a str = crate::whitespace::ws(nom::sequence::preceded(
104            nom::combinator::opt(nom::character::complete::char('_')),
105            nom::combinator::recognize(nom::sequence::tuple((
106                nom::bytes::complete::take_while1(nom::AsChar::is_alphanum),
107                nom::bytes::complete::take_while(is_alphanum_underscore_dash),
108            )))
109        )),
110    )
111
112    #[derive(Copy)]
114    struct Default<'a> {
115        assign: term!(=),
116        value: DefaultValue<'a>,
117    }
118
119    struct Docstring(
121        String = docstring,
122    )
123}
124
125#[cfg(test)]
126mod test {
127    use super::*;
128
129    test!(should_parse_optional_present { "one" =>
130        "";
131        Option<Identifier>;
132        is_some();
133    });
134
135    test!(should_parse_optional_not_present { "" =>
136        "";
137        Option<Identifier>;
138        is_none();
139    });
140
141    test!(should_parse_boxed { "one" =>
142        "";
143        Box<Identifier>;
144    });
145
146    test!(should_parse_vec { "one two three" =>
147        "";
148        Vec<Identifier>;
149        len() == 3;
150    });
151
152    test!(should_parse_parenthesized { "( one )" =>
153        "";
154        Parenthesized<Identifier>;
155        body.0 == "one";
156    });
157
158    test!(should_parse_bracketed { "[ one ]" =>
159        "";
160        Bracketed<Identifier>;
161        body.0 == "one";
162    });
163
164    test!(should_parse_braced { "{ one }" =>
165        "";
166        Braced<Identifier>;
167        body.0 == "one";
168    });
169
170    test!(should_parse_generics { "<one>" =>
171        "";
172        Generics<Identifier>;
173        body.0 == "one";
174    });
175
176    test!(should_parse_generics_two { "<one, two>" =>
177        "";
178        Generics<(Identifier, term!(,), Identifier)> =>
179            Generics {
180                open_angle: term!(<),
181                body: (Identifier("one"), term!(,), Identifier("two")),
182                close_angle: term!(>),
183            }
184    });
185
186    test!(should_parse_comma_separated_values { "one, two, three" =>
187        "";
188        Punctuated<Identifier, term!(,)>;
189        list.len() == 3;
190    });
191
192    test!(err should_not_parse_comma_separated_values_empty { "" =>
193        PunctuatedNonEmpty<Identifier, term!(,)>
194    });
195
196    test!(should_parse_identifier { "hello" =>
197        "";
198        Identifier;
199        0 == "hello";
200    });
201
202    test!(should_parse_numbered_identifier { "hello5" =>
203        "";
204        Identifier;
205        0 == "hello5";
206    });
207
208    test!(should_parse_underscored_identifier { "_hello_" =>
209        "";
210        Identifier;
211        0 == "hello_";
212    });
213
214    test!(should_parse_identifier_surrounding_with_spaces { "  hello  " =>
215        "";
216        Identifier;
217        0 == "hello";
218    });
219
220    test!(should_parse_identifier_preceding_others { "hello  note" =>
221        "note";
222        Identifier;
223        0 == "hello";
224    });
225
226    test!(should_parse_identifier_attached_to_symbol { "hello=" =>
227        "=";
228        Identifier;
229        0 == "hello";
230    });
231
232    test!(should_parse_docstring { "///hello world\n" =>
233        "";
234        Docstring;
235        0 == "hello world";
236    });
237
238    test!(should_parse_multiline_docstring { "///hello\n///world\n" =>
239        "";
240        Docstring;
241        0 == "hello\nworld";
242    });
243
244    test!(should_parse_multiline_indented_docstring { "///hello\n  ///world\n" =>
245        "";
246        Docstring;
247        0 == "hello\nworld";
248    });
249
250    test!(should_not_parse_docstring_with_comments { "///hello\n//comment1\n///world\n" =>
251        "//comment1\n///world\n";
252        Docstring;
253        0 == "hello";
254    });
255
256    test!(err should_not_parse_not_docstring { "" =>
257        Docstring
258    });
259}