1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use nom::{
    bytes::complete::{take_while, take_while1},
    character::complete::char,
    combinator::{cut, map, opt},
    multi::separated_list0,
    sequence::{pair, preceded, terminated},
    IResult,
};
use nom_locate::LocatedSpan;

pub type Span<'a> = LocatedSpan<&'a str>;

const WHITSPACE: &str = " \t\r\n";
const ALPHA_EXTRA: &str = "_";

pub fn ws(input: Span) -> IResult<Span, Span> {
    take_while(move |c| WHITSPACE.contains(c))(input)
}

pub fn ws1(input: Span) -> IResult<Span, Span> {
    take_while1(move |c| WHITSPACE.contains(c))(input)
}

pub fn trailing_comma(input: Span) -> IResult<Span, Option<char>> {
    opt(preceded(ws, char(',')))(input)
}

pub fn parse_identifier(input: Span) -> IResult<Span, String> {
    map(
        pair(
            take_while1(move |c: char| c.is_ascii_alphabetic()),
            take_while(move |c: char| c.is_ascii_alphanumeric() || ALPHA_EXTRA.contains(c)),
        ),
        |t| format!("{}{}", t.0, t.1),
    )(input)
}

fn parse_generics(input: Span) -> IResult<Span, Vec<String>> {
    map(
        opt(preceded(
            preceded(ws, char('<')),
            cut(terminated(
                separated_list0(parse_field_separator, preceded(ws, parse_identifier)),
                preceded(trailing_comma, preceded(ws, char('>'))),
            )),
        )),
        |v| match v {
            Some(v) => v,
            None => Vec::with_capacity(0),
        },
    )(input)
}

pub fn parse_identifier_with_generics(input: Span) -> IResult<Span, (String, Vec<String>)> {
    pair(parse_identifier, parse_generics)(input)
}

#[cfg(test)]
pub(crate) fn assert_parse<'a, T: std::fmt::Debug + PartialEq>(
    output: IResult<LocatedSpan<&'a str>, T>,
    expected_value: T,
) {
    assert!(output.is_ok(), "{:?}", output);
    let output = output.unwrap();
    assert_eq!(output.0.fragment(), &"");
    assert_eq!(output.1, expected_value);
}

#[test]
fn test_parse_identifier() {
    assert_parse(parse_identifier(Span::new("test")), "test".to_string());
    assert_parse(
        parse_identifier(Span::new("test123")),
        "test123".to_string(),
    );
}

#[test]
fn test_parse_identifier_invalid() {
    use nom::error::ErrorKind;
    assert_eq!(
        parse_identifier(Span::new("123test")),
        Err(nom::Err::Error(nom::error::Error {
            input: Span::new("123test"),
            code: ErrorKind::TakeWhile1
        }))
    );
    assert_eq!(
        parse_identifier(Span::new("_test")),
        Err(nom::Err::Error(nom::error::Error {
            input: Span::new("_test"),
            code: ErrorKind::TakeWhile1
        }))
    );
}

pub fn parse_field_separator(input: Span) -> IResult<Span, char> {
    preceded(ws, char(','))(input)
}