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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/// The functions in this module are common for parsing AT commands and responses and the
/// arguments contained in them.
///
/// The next_* functions take a Pairs, an iterator over all the child syntax
/// nodes of a given syntactic element, and check to make sure the next element is of the
/// correct type or Rule.  The rest of the functions recursively convert untyped pest trees to
/// the appropriate typed definition types.
///
/// Additionally, there are functions for parsing common scalars such as integers, strings and
/// command names.
use {
    pest::{
        error::Error as PestError,
        iterators::{Pair, Pairs},
        RuleType,
    },
    std::num::ParseIntError,
    thiserror::Error,
};

#[derive(Debug, Error)]
pub enum ParseError<Rule: RuleType> {
    #[error("Unable to use pest to parse {string:?}: {pest_error:?}")]
    PestParseFailure { string: String, pest_error: PestError<Rule> },
    #[error("Expected one of {expected_rules:?}, got nothing.")]
    NextRuleMissing { expected_rules: Vec<Rule> },
    #[error("Expected one of {expected_rules:?}, got {actual_rule:?}.")]
    NextRuleUnexpected { expected_rules: Vec<Rule>, actual_rule: Rule },
    #[error("Unable to parse \"{string:?}\" as an integer: {error:?}.")]
    InvalidInteger { string: String, error: ParseIntError },
    #[error("Unknown character after AT: \"{0}\"")]
    UnknownExtensionCharacter(String),
}

pub type ParseResult<T, R> = std::result::Result<T, ParseError<R>>;

/// Get the next Pair syntax node out of an iterator if it is matched by one of the expected Rules.
/// Otherwise, fail.
pub fn next_match_one_of<'a, Rule: RuleType>(
    pairs: &mut Pairs<'a, Rule>,
    expected_rules: Vec<Rule>,
) -> ParseResult<Pair<'a, Rule>, Rule> {
    let pair_result = pairs.next();
    let pair = pair_result
        .ok_or(ParseError::NextRuleMissing { expected_rules: expected_rules.clone() })?;

    let pair_rule = pair.as_rule();
    if !expected_rules.contains(&pair_rule) {
        return Err(ParseError::NextRuleUnexpected {
            expected_rules: expected_rules.clone(),
            actual_rule: pair_rule,
        });
    }

    Ok(pair)
}

/// Get the next Pair syntax node out of an iterator if it is matched by the expected Rule.
/// Otherwise, fail.
pub fn next_match<'a, Rule: RuleType>(
    pairs: &mut Pairs<'a, Rule>,
    expected_rule: Rule,
) -> ParseResult<Pair<'a, Rule>, Rule> {
    next_match_one_of(pairs, vec![expected_rule])
}

/// Get the next Pair syntax node out of an interator if it exists.  If it doesn't exist, return
/// None.  If it does exist and is not matched by the specified Rule, fail.
pub fn next_match_option<'a, Rule: RuleType>(
    pairs: &mut Pairs<'a, Rule>,
    expected_rule: Rule,
) -> ParseResult<Option<Pair<'a, Rule>>, Rule> {
    if !pairs.peek().is_some() {
        Ok(None)
    } else {
        next_match(pairs, expected_rule).map(|pair| Some(pair))
    }
}

/// Continue getting the next Pair syntax node of the iterator as long as they are  matched by the
/// expected Rule.  Return a vector of matching Pairs.
pub fn next_match_rep<'a, Rule: RuleType>(
    pairs: &mut Pairs<'a, Rule>,
    expected_rule: Rule,
) -> Vec<Pair<'a, Rule>> {
    let is_correct_rule = |pair_option: Option<Pair<'a, Rule>>| match pair_option {
        Some(pair) => pair.as_rule() == expected_rule,
        None => false,
    };

    let mut vector = Vec::new();
    while is_correct_rule(pairs.peek()) {
        let pair = pairs.next().unwrap();
        vector.push(pair);
    }

    vector
}

/// Parse a pair to a string.  The caller must ensure that this is actually matched by a
/// rule that can be parsed to a valid AT command string.
pub fn parse_string<Rule: RuleType>(string: Pair<'_, Rule>) -> ParseResult<String, Rule> {
    Ok(string.as_span().as_str().to_string())
}

/// Parse a pair to a string.  The caller must ensure that this is actually matched by a
/// rule that can be parsed to a valid integer.
pub fn parse_integer<Rule: RuleType>(integer: Pair<'_, Rule>) -> ParseResult<i64, Rule> {
    let str = integer.as_span().as_str();
    str.parse().map_err(|err| ParseError::InvalidInteger { string: str.to_string(), error: err })
}

/// Parse a pair to a string.  The caller must ensure that this is actually matched by a
/// rule that can be parsed to a valid AT command name.
pub fn parse_name<Rule: RuleType>(name: Pair<'_, Rule>) -> ParseResult<String, Rule> {
    Ok(name.as_span().as_str().to_string())
}