thrift_parser/
field.rs

1use nom::branch::alt;
2use nom::bytes::complete::tag;
3use nom::character::complete::char as cchar;
4use nom::combinator::{map, opt};
5use nom::sequence::{delimited, terminated, tuple};
6use nom::IResult;
7
8use crate::basic::{Identifier, IdentifierRef, ListSeparator, Separator};
9use crate::constant::{ConstValue, ConstValueRef, IntConstant};
10use crate::types::{FieldType, FieldTypeRef};
11use crate::Parser;
12
13// Field           ::=  FieldID? FieldReq? FieldType Identifier ('=' ConstValue)? ListSeparator?
14// FieldID         ::=  IntConstant ':'
15// FieldReq        ::=  'required' | 'optional'
16// Note: XsdFieldOptions is not supported in out impl and strongly discouraged in official docs.
17#[derive(Debug, Clone, PartialEq)]
18pub struct FieldRef<'a> {
19    pub id: Option<IntConstant>,
20    pub required: Option<bool>,
21    pub type_: FieldTypeRef<'a>,
22    pub name: IdentifierRef<'a>,
23    pub default: Option<ConstValueRef<'a>>,
24}
25
26impl<'a> Parser<'a> for FieldRef<'a> {
27    fn parse(input: &'a str) -> IResult<&'a str, Self> {
28        map(
29            tuple((
30                opt(terminated(
31                    IntConstant::parse,
32                    delimited(opt(Separator::parse), cchar(':'), opt(Separator::parse)),
33                )),
34                opt(terminated(
35                    alt((
36                        map(tag("required"), |_| true),
37                        map(tag("optional"), |_| false),
38                    )),
39                    Separator::parse,
40                )),
41                terminated(FieldTypeRef::parse, Separator::parse),
42                terminated(IdentifierRef::parse, opt(Separator::parse)),
43                opt(map(
44                    tuple((cchar('='), opt(Separator::parse), ConstValueRef::parse)),
45                    |(_, _, cv)| cv,
46                )),
47                opt(Separator::parse),
48                opt(ListSeparator::parse),
49            )),
50            |(id, required, type_, name, default, _, _)| Self {
51                id,
52                required,
53                type_,
54                name,
55                default,
56            },
57        )(input)
58    }
59}
60
61#[derive(Debug, Clone, PartialEq)]
62pub struct Field {
63    pub id: Option<IntConstant>,
64    pub required: Option<bool>,
65    pub type_: FieldType,
66    pub name: Identifier,
67    pub default: Option<ConstValue>,
68}
69
70impl<'a> From<FieldRef<'a>> for Field {
71    fn from(r: FieldRef<'a>) -> Self {
72        Self {
73            id: r.id,
74            required: r.required,
75            type_: r.type_.into(),
76            name: r.name.into(),
77            default: r.default.map(Into::into),
78        }
79    }
80}
81
82impl<'a> Parser<'a> for Field {
83    fn parse(input: &'a str) -> IResult<&'a str, Self> {
84        FieldRef::parse(input).map(|(remains, parsed)| (remains, parsed.into()))
85    }
86}
87
88#[cfg(test)]
89mod test {
90    use crate::basic::LiteralRef;
91
92    use super::*;
93
94    #[test]
95    fn test_field() {
96        let expected = FieldRef {
97            id: None,
98            required: Some(true),
99            type_: FieldTypeRef::String,
100            name: IdentifierRef::from("name"),
101            default: Some(ConstValueRef::Literal(LiteralRef::from("ihciah"))),
102        };
103        assert_eq!(
104            FieldRef::parse("required  string  name  =  'ihciah'")
105                .unwrap()
106                .1,
107            expected
108        );
109        assert_eq!(
110            FieldRef::parse("required string name='ihciah';").unwrap().1,
111            expected
112        );
113
114        let expected = FieldRef {
115            id: Some(IntConstant::from(3)),
116            required: Some(true),
117            type_: FieldTypeRef::String,
118            name: IdentifierRef::from("name"),
119            default: Some(ConstValueRef::Literal(LiteralRef::from("ihciah"))),
120        };
121        assert_eq!(
122            FieldRef::parse("3 : required  string  name  =  'ihciah'")
123                .unwrap()
124                .1,
125            expected
126        );
127        assert_eq!(
128            FieldRef::parse("3:required string name='ihciah';")
129                .unwrap()
130                .1,
131            expected
132        );
133    }
134}