at_commands/parser/
common.rs

1// Copyright 2021 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
5/// The functions in this module are common for parsing AT commands and responses and the
6/// arguments contained in them.
7///
8/// The next_* functions take a Pairs, an iterator over all the child syntax
9/// nodes of a given syntactic element, and check to make sure the next element is of the
10/// correct type or Rule.  The rest of the functions recursively convert untyped pest trees to
11/// the appropriate typed definition types.
12///
13/// Additionally, there are functions for parsing common scalars such as integers, strings and
14/// command names.
15use {
16    pest::{
17        error::Error as PestError,
18        iterators::{Pair, Pairs},
19        RuleType,
20    },
21    std::num::ParseIntError,
22    thiserror::Error,
23};
24
25#[derive(Debug, Error)]
26pub enum ParseError<Rule: RuleType> {
27    #[error("Unable to use pest to parse {string:?}: {pest_error:?}")]
28    PestParseFailure { string: String, pest_error: PestError<Rule> },
29    #[error("Expected one of {expected_rules:?}, got nothing.")]
30    NextRuleMissing { expected_rules: Vec<Rule> },
31    #[error("Expected one of {expected_rules:?}, got {actual_rule:?}.")]
32    NextRuleUnexpected { expected_rules: Vec<Rule>, actual_rule: Rule },
33    #[error("Unable to parse \"{string:?}\" as an integer: {error:?}.")]
34    InvalidInteger { string: String, error: ParseIntError },
35    #[error("Unknown character after AT: \"{0}\"")]
36    UnknownExtensionCharacter(String),
37}
38
39pub type ParseResult<T, R> = std::result::Result<T, Box<ParseError<R>>>;
40
41/// Get the next Pair syntax node out of an iterator if it is matched by one of the expected Rules.
42/// Otherwise, fail.
43pub fn next_match_one_of<'a, Rule: RuleType>(
44    pairs: &mut Pairs<'a, Rule>,
45    expected_rules: Vec<Rule>,
46) -> ParseResult<Pair<'a, Rule>, Rule> {
47    let pair_result = pairs.next();
48    let pair = pair_result
49        .ok_or_else(|| ParseError::NextRuleMissing { expected_rules: expected_rules.clone() })?;
50
51    let pair_rule = pair.as_rule();
52    if !expected_rules.contains(&pair_rule) {
53        return Err(Box::new(ParseError::NextRuleUnexpected {
54            expected_rules: expected_rules,
55            actual_rule: pair_rule,
56        }));
57    }
58
59    Ok(pair)
60}
61
62/// Get the next Pair syntax node out of an iterator if it is matched by the expected Rule.
63/// Otherwise, fail.
64pub fn next_match<'a, Rule: RuleType>(
65    pairs: &mut Pairs<'a, Rule>,
66    expected_rule: Rule,
67) -> ParseResult<Pair<'a, Rule>, Rule> {
68    next_match_one_of(pairs, vec![expected_rule])
69}
70
71/// Get the next Pair syntax node out of an interator if it exists.  If it doesn't exist, return
72/// None.  If it does exist and is not matched by the specified Rule, fail.
73pub fn next_match_option<'a, Rule: RuleType>(
74    pairs: &mut Pairs<'a, Rule>,
75    expected_rule: Rule,
76) -> ParseResult<Option<Pair<'a, Rule>>, Rule> {
77    if !pairs.peek().is_some() {
78        Ok(None)
79    } else {
80        next_match(pairs, expected_rule).map(|pair| Some(pair))
81    }
82}
83
84/// Continue getting the next Pair syntax node of the iterator as long as they are  matched by the
85/// expected Rule.  Return a vector of matching Pairs.
86pub fn next_match_rep<'a, Rule: RuleType>(
87    pairs: &mut Pairs<'a, Rule>,
88    expected_rule: Rule,
89) -> Vec<Pair<'a, Rule>> {
90    let is_correct_rule = |pair_option: Option<Pair<'a, Rule>>| match pair_option {
91        Some(pair) => pair.as_rule() == expected_rule,
92        None => false,
93    };
94
95    let mut vector = Vec::new();
96    while is_correct_rule(pairs.peek()) {
97        let pair = pairs.next().unwrap();
98        vector.push(pair);
99    }
100
101    vector
102}
103
104/// Parse a pair to a string.  The caller must ensure that this is actually matched by a
105/// rule that can be parsed to a valid AT command string.
106pub fn parse_string<Rule: RuleType>(string: Pair<'_, Rule>) -> ParseResult<String, Rule> {
107    Ok(string.as_span().as_str().to_string())
108}
109
110/// Parse a pair to a string.  The caller must ensure that this is actually matched by a
111/// rule that can be parsed to a valid integer.
112pub fn parse_integer<Rule: RuleType>(integer: Pair<'_, Rule>) -> ParseResult<i64, Rule> {
113    let str = integer.as_span().as_str();
114    str.parse()
115        .map_err(|err| Box::new(ParseError::InvalidInteger { string: str.to_string(), error: err }))
116}
117
118/// Parse a pair to a string.  The caller must ensure that this is actually matched by a
119/// rule that can be parsed to a valid AT command name.
120pub fn parse_name<Rule: RuleType>(name: Pair<'_, Rule>) -> ParseResult<String, Rule> {
121    Ok(name.as_span().as_str().to_string())
122}