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
100
101
102
103
104
105
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 crate::basic::Literal;

    use super::*;

    #[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
        );
    }
}