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
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::char as cchar;
use nom::combinator::{map, opt};
use nom::sequence::{delimited, terminated, tuple};
use nom::IResult;

use crate::basic::{Identifier, ListSeparator, Separator};
use crate::constant::{ConstValue, IntConstant};
use crate::types::FieldType;
use crate::Parser;

// Field           ::=  FieldID? FieldReq? FieldType Identifier ('=' ConstValue)? ListSeparator?
// FieldID         ::=  IntConstant ':'
// FieldReq        ::=  'required' | 'optional'
// Note: XsdFieldOptions is not supported in out impl and strongly discouraged in official docs.
#[derive(Debug, Clone, PartialEq)]
pub struct Field<'a> {
    pub id: Option<IntConstant>,
    pub required: Option<bool>,
    pub type_: FieldType<'a>,
    pub name: Identifier<'a>,
    pub default: Option<ConstValue<'a>>,
}

impl<'a> Parser<'a> for Field<'a> {
    fn parse(input: &'a str) -> IResult<&'a str, Self> {
        map(
            tuple((
                opt(terminated(
                    IntConstant::parse,
                    delimited(opt(Separator::parse), cchar(':'), opt(Separator::parse)),
                )),
                opt(terminated(
                    alt((
                        map(tag("required"), |_| true),
                        map(tag("optional"), |_| false),
                    )),
                    Separator::parse,
                )),
                terminated(FieldType::parse, Separator::parse),
                terminated(Identifier::parse, opt(Separator::parse)),
                opt(map(
                    tuple((cchar('='), opt(Separator::parse), ConstValue::parse)),
                    |(_, _, cv)| cv,
                )),
                opt(Separator::parse),
                opt(ListSeparator::parse),
            )),
            |(id, required, type_, name, default, _, _)| Self {
                id,
                required,
                type_,
                name,
                default,
            },
        )(input)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::basic::Literal;

    #[test]
    fn test_field() {
        let expected = Field {
            id: None,
            required: Some(true),
            type_: FieldType::String,
            name: Identifier::from("name"),
            default: Some(ConstValue::Literal(Literal::from("ihciah")))
        };
        assert_eq!(Field::parse("required  string  name  =  'ihciah'").unwrap().1, expected);
        assert_eq!(Field::parse("required string name='ihciah';").unwrap().1, expected);

        let expected = Field {
            id: Some(IntConstant::from(3)),
            required: Some(true),
            type_: FieldType::String,
            name: Identifier::from("name"),
            default: Some(ConstValue::Literal(Literal::from("ihciah")))
        };
        assert_eq!(Field::parse("3 : required  string  name  =  'ihciah'").unwrap().1, expected);
        assert_eq!(Field::parse("3:required string name='ihciah';").unwrap().1, expected);
    }
}