webwire_cli/idl/
common.rs1use nom::{
2 bytes::complete::{take_while, take_while1},
3 character::complete::char,
4 combinator::{cut, map, opt},
5 multi::separated_list0,
6 sequence::{pair, preceded, terminated},
7 IResult,
8};
9use nom_locate::LocatedSpan;
10
11pub type Span<'a> = LocatedSpan<&'a str>;
12
13const WHITSPACE: &str = " \t\r\n";
14const ALPHA_EXTRA: &str = "_";
15
16pub fn ws(input: Span) -> IResult<Span, Span> {
17 take_while(move |c| WHITSPACE.contains(c))(input)
18}
19
20pub fn ws1(input: Span) -> IResult<Span, Span> {
21 take_while1(move |c| WHITSPACE.contains(c))(input)
22}
23
24pub fn trailing_comma(input: Span) -> IResult<Span, Option<char>> {
25 opt(preceded(ws, char(',')))(input)
26}
27
28pub fn parse_identifier(input: Span) -> IResult<Span, String> {
29 map(
30 pair(
31 take_while1(move |c: char| c.is_ascii_alphabetic()),
32 take_while(move |c: char| c.is_ascii_alphanumeric() || ALPHA_EXTRA.contains(c)),
33 ),
34 |t| format!("{}{}", t.0, t.1),
35 )(input)
36}
37
38fn parse_generics(input: Span) -> IResult<Span, Vec<String>> {
39 map(
40 opt(preceded(
41 preceded(ws, char('<')),
42 cut(terminated(
43 separated_list0(parse_field_separator, preceded(ws, parse_identifier)),
44 preceded(trailing_comma, preceded(ws, char('>'))),
45 )),
46 )),
47 |v| match v {
48 Some(v) => v,
49 None => Vec::with_capacity(0),
50 },
51 )(input)
52}
53
54pub fn parse_identifier_with_generics(input: Span) -> IResult<Span, (String, Vec<String>)> {
55 pair(parse_identifier, parse_generics)(input)
56}
57
58#[cfg(test)]
59pub(crate) fn assert_parse<'a, T: std::fmt::Debug + PartialEq>(
60 output: IResult<LocatedSpan<&'a str>, T>,
61 expected_value: T,
62) {
63 assert!(output.is_ok(), "{:?}", output);
64 let output = output.unwrap();
65 assert_eq!(output.0.fragment(), &"");
66 assert_eq!(output.1, expected_value);
67}
68
69#[test]
70fn test_parse_identifier() {
71 assert_parse(parse_identifier(Span::new("test")), "test".to_string());
72 assert_parse(
73 parse_identifier(Span::new("test123")),
74 "test123".to_string(),
75 );
76}
77
78#[test]
79fn test_parse_identifier_invalid() {
80 use nom::error::ErrorKind;
81 assert_eq!(
82 parse_identifier(Span::new("123test")),
83 Err(nom::Err::Error(nom::error::Error {
84 input: Span::new("123test"),
85 code: ErrorKind::TakeWhile1
86 }))
87 );
88 assert_eq!(
89 parse_identifier(Span::new("_test")),
90 Err(nom::Err::Error(nom::error::Error {
91 input: Span::new("_test"),
92 code: ErrorKind::TakeWhile1
93 }))
94 );
95}
96
97pub fn parse_field_separator(input: Span) -> IResult<Span, char> {
98 preceded(ws, char(','))(input)
99}