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()), }, ], ); } }