bind/debugger/
device_specification.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::parser::common::{
6    compound_identifier, condition_value, many_until_eof, map_err, ws, BindParserError,
7    CompoundIdentifier, NomSpan, Value,
8};
9use nom::bytes::complete::tag;
10use nom::sequence::separated_pair;
11use nom::{IResult, Parser};
12use std::str::FromStr;
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct DeviceSpecification {
16    pub properties: Vec<Property>,
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct Property {
21    pub key: CompoundIdentifier,
22    pub value: Value,
23}
24
25impl FromStr for DeviceSpecification {
26    type Err = BindParserError;
27
28    fn from_str(input: &str) -> Result<Self, Self::Err> {
29        match device_specification(NomSpan::new(input)) {
30            Ok((_, spec)) => Ok(spec),
31            Err(nom::Err::Error(e)) => Err(e),
32            Err(nom::Err::Failure(e)) => Err(e),
33            Err(nom::Err::Incomplete(_)) => {
34                unreachable!("Parser should never generate Incomplete errors")
35            }
36        }
37    }
38}
39
40impl DeviceSpecification {
41    pub fn new() -> Self {
42        DeviceSpecification { properties: Vec::new() }
43    }
44
45    pub fn add_property(&mut self, key: &str, value: &str) -> Result<(), BindParserError> {
46        match property_from_pair(NomSpan::new(key), NomSpan::new(value)) {
47            Ok((_, property)) => {
48                self.properties.push(property);
49                Ok(())
50            }
51            Err(nom::Err::Error(e)) => Err(e),
52            Err(nom::Err::Failure(e)) => Err(e),
53            Err(nom::Err::Incomplete(_)) => {
54                unreachable!("Parser should never generate Incomplete errors")
55            }
56        }
57    }
58}
59
60fn property_from_pair<'a, 'b>(
61    key: NomSpan<'a>,
62    value: NomSpan<'b>,
63) -> IResult<(NomSpan<'a>, NomSpan<'b>), Property, BindParserError> {
64    let (key_remaining, key) = compound_identifier(key)?;
65    if !key_remaining.fragment().is_empty() {
66        return Err(nom::Err::Error(BindParserError::Eof(key_remaining.fragment().to_string())));
67    }
68    let (value_remaining, value) = condition_value(value)?;
69    if !value_remaining.fragment().is_empty() {
70        return Err(nom::Err::Error(BindParserError::Eof(value_remaining.fragment().to_string())));
71    }
72    Ok(((key_remaining, value_remaining), Property { key, value }))
73}
74
75fn property(input: NomSpan) -> IResult<NomSpan, Property, BindParserError> {
76    let key = ws(compound_identifier);
77    let separator = ws(map_err(tag("="), BindParserError::Assignment));
78    let (input, (key, value)) = separated_pair(key, separator, condition_value).parse(input)?;
79    Ok((input, Property { key, value }))
80}
81
82fn device_specification(input: NomSpan) -> IResult<NomSpan, DeviceSpecification, BindParserError> {
83    let (input, properties) = many_until_eof(ws(property)).parse(input)?;
84    Ok((input, DeviceSpecification { properties }))
85}
86
87#[cfg(test)]
88mod test {
89    use super::*;
90    use crate::make_identifier;
91    use crate::parser::common::test::check_result;
92
93    mod properties {
94        use super::*;
95
96        #[test]
97        fn simple() {
98            check_result(
99                property(NomSpan::new("abc = 5")),
100                "",
101                Property { key: make_identifier!["abc"], value: Value::NumericLiteral(5) },
102            );
103        }
104
105        #[test]
106        fn simple_from_pair() {
107            assert_eq!(
108                property_from_pair(NomSpan::new("abc"), NomSpan::new("5")).unwrap().1,
109                Property { key: make_identifier!["abc"], value: Value::NumericLiteral(5) },
110            );
111        }
112
113        #[test]
114        fn invalid() {
115            assert_eq!(
116                property(NomSpan::new("abc 5")),
117                Err(nom::Err::Error(BindParserError::Assignment("5".to_string())))
118            );
119
120            assert_eq!(
121                property(NomSpan::new("abc =")),
122                Err(nom::Err::Error(BindParserError::ConditionValue("".to_string())))
123            );
124
125            assert_eq!(
126                property(NomSpan::new("= 5")),
127                Err(nom::Err::Error(BindParserError::Identifier("= 5".to_string())))
128            );
129        }
130
131        #[test]
132        fn invalid_from_pair() {
133            assert_eq!(
134                property_from_pair(NomSpan::new("abc def"), NomSpan::new("5")),
135                Err(nom::Err::Error(BindParserError::Eof(" def".to_string())))
136            );
137
138            assert_eq!(
139                property_from_pair(NomSpan::new("_abc"), NomSpan::new("5")),
140                Err(nom::Err::Error(BindParserError::Identifier("_abc".to_string())))
141            );
142
143            assert_eq!(
144                property_from_pair(NomSpan::new("abc"), NomSpan::new("5 42")),
145                Err(nom::Err::Error(BindParserError::Eof(" 42".to_string())))
146            );
147
148            assert_eq!(
149                property_from_pair(NomSpan::new("abc"), NomSpan::new("@")),
150                Err(nom::Err::Error(BindParserError::ConditionValue("@".to_string())))
151            );
152        }
153
154        #[test]
155        fn empty() {
156            assert_eq!(
157                property(NomSpan::new("")),
158                Err(nom::Err::Error(BindParserError::Identifier("".to_string())))
159            );
160        }
161
162        #[test]
163        fn empty_from_pair() {
164            assert_eq!(
165                property_from_pair(NomSpan::new(""), NomSpan::new("5")),
166                Err(nom::Err::Error(BindParserError::Identifier("".to_string())))
167            );
168
169            assert_eq!(
170                property_from_pair(NomSpan::new("abc"), NomSpan::new("")),
171                Err(nom::Err::Error(BindParserError::ConditionValue("".to_string())))
172            );
173        }
174    }
175
176    mod device_specifications {
177        use super::*;
178
179        #[test]
180        fn simple() {
181            check_result(
182                device_specification(NomSpan::new("abc = 5\nxyz = true")),
183                "",
184                DeviceSpecification {
185                    properties: vec![
186                        Property { key: make_identifier!["abc"], value: Value::NumericLiteral(5) },
187                        Property { key: make_identifier!["xyz"], value: Value::BoolLiteral(true) },
188                    ],
189                },
190            );
191        }
192
193        #[test]
194        fn empty() {
195            check_result(
196                device_specification(NomSpan::new("")),
197                "",
198                DeviceSpecification { properties: Vec::new() },
199            );
200        }
201    }
202}