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
use nom::{
    character::complete::char,
    combinator::{cut, map},
    error::context,
    multi::separated_list0,
    sequence::{preceded, separated_pair, terminated},
    IResult,
};

#[cfg(test)]
use crate::idl::common::assert_parse;
use crate::idl::common::{parse_field_separator, parse_identifier, trailing_comma, ws, Span};
use crate::idl::r#value::{parse_value, Value};

#[derive(Debug, PartialEq)]
pub struct FieldOption {
    pub name: String,
    pub value: Value,
}

pub fn parse_field_options(input: Span) -> IResult<Span, Vec<FieldOption>> {
    context(
        "options",
        preceded(
            preceded(ws, char('(')),
            cut(terminated(
                separated_list0(parse_field_separator, parse_field_option),
                preceded(trailing_comma, preceded(ws, char(')'))),
            )),
        ),
    )(input)
}

fn parse_field_option(input: Span) -> IResult<Span, FieldOption> {
    map(
        separated_pair(
            preceded(ws, parse_identifier),
            preceded(ws, char('=')),
            preceded(ws, parse_value),
        ),
        |(name, value)| FieldOption { name, value },
    )(input)
}

#[test]
fn test_parse_field_options_0() {
    let contents = ["()", "( )", "(,)", "( ,)", "(, )"];
    for content in contents.iter() {
        assert_parse(parse_field_options(Span::new(content)), vec![]);
    }
}

#[test]
fn test_parse_field_options_1() {
    let contents = [
        "(foo=42)",
        "(foo= 42)",
        "(foo=42 )",
        "( foo=42)",
        "(foo=42,)",
    ];
    for content in contents.iter() {
        assert_parse(
            parse_field_options(Span::new(content)),
            vec![FieldOption {
                name: "foo".to_owned(),
                value: Value::Integer(42),
            }],
        );
    }
}

#[test]
fn test_parse_field_options_2() {
    let contents = [
        "(foo=42,bar=\"epic\")",
        "(foo= 42, bar= \"epic\")",
        "( foo=42,bar=\"epic\" )",
        "( foo= 42, bar= \"epic\" )",
        "( foo= 42, bar= \"epic\", )",
    ];
    for content in contents.iter() {
        assert_parse(
            parse_field_options(Span::new(content)),
            vec![
                FieldOption {
                    name: "foo".to_owned(),
                    value: Value::Integer(42),
                },
                FieldOption {
                    name: "bar".to_owned(),
                    value: Value::String("epic".to_string()),
                },
            ],
        );
    }
}