at_commands/parser/
response_parser.rs

1// Copyright 2020 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 convert the untyped pest Pair syntax nodes to typed AT response AST nodes.
6///
7/// These functions should not fail, but report failures from the next_* methods if the
8/// expected element is not found.  This should only happen if the code here does not correctly
9/// match the parse tree defined in response_grammar.rs.
10use {
11    crate::lowlevel::{HardcodedError, Response},
12    crate::parser::{
13        arguments_parser::ArgumentsParser,
14        common::{
15            ParseError, ParseResult, next_match, next_match_one_of, parse_integer, parse_name,
16            parse_string,
17        },
18        response_grammar::{Grammar, Rule},
19    },
20    pest::{Parser, iterators::Pair},
21};
22
23static ARGUMENTS_PARSER: ArgumentsParser<Rule> = ArgumentsParser {
24    argument_list: Rule::argument_list,
25    argument: Rule::argument,
26    arguments: Rule::arguments,
27    key_value_argument: Rule::key_value_argument,
28    optional_argument_delimiter: Rule::optional_argument_delimiter,
29    optional_argument_terminator: Rule::optional_argument_terminator,
30    parenthesized_argument_lists: Rule::parenthesized_argument_lists,
31    primitive_argument: Rule::primitive_argument,
32};
33
34pub fn parse(string: &str) -> ParseResult<Response, Rule> {
35    let mut parsed = Grammar::parse(Rule::input, string).map_err(|pest_error| {
36        ParseError::PestParseFailure { string: string.into(), pest_error: Box::new(pest_error) }
37    })?;
38
39    let input = next_match(&mut parsed, Rule::input)?;
40    let mut input_elements = input.into_inner();
41    let response = next_match(&mut input_elements, Rule::response)?;
42
43    parse_response(response)
44}
45
46fn parse_response(response: Pair<'_, Rule>) -> ParseResult<Response, Rule> {
47    let mut response_elements = response.into_inner();
48    let response_variant = next_match_one_of(
49        &mut response_elements,
50        vec![Rule::ok, Rule::error, Rule::hardcoded_error, Rule::cme_error, Rule::success],
51    )?;
52
53    let parsed_response = match response_variant.as_rule() {
54        Rule::ok => Response::Ok,
55        Rule::error => Response::Error,
56        Rule::hardcoded_error => parse_hardcoded_error(response_variant)?,
57        Rule::cme_error => parse_cme_error(response_variant)?,
58        Rule::success => parse_success(response_variant)?,
59        // This is unreachable since next_match_one_of only returns success if one of the rules
60        // passed into it matches; otherwise it returns Err and this method will return early
61        // before reaching this point.
62        _ => unreachable!(),
63    };
64
65    Ok(parsed_response)
66}
67
68fn parse_hardcoded_error(hardcoded_error: Pair<'_, Rule>) -> ParseResult<Response, Rule> {
69    let error_string = parse_string(hardcoded_error)?;
70
71    let parsed_error = match error_string.as_str() {
72        "NO CARRIER" => HardcodedError::NoCarrier,
73        "BUSY" => HardcodedError::Busy,
74        "NO ANSWER" => HardcodedError::NoAnswer,
75        "DELAYED" => HardcodedError::Delayed,
76        "BLACKLIST" => HardcodedError::Blacklist,
77        // This is unreachable since these are the only strings that match the harcoded_error rule.
78        // For parse_response to call this method, hardcoded_error must have matched this span of the
79        // parsed response, so only the above strings are possible matches.
80        _ => unreachable!(),
81    };
82
83    Ok(Response::HardcodedError(parsed_error))
84}
85
86fn parse_cme_error(cme_error: Pair<'_, Rule>) -> ParseResult<Response, Rule> {
87    let mut cme_error_elements = cme_error.into_inner();
88
89    let error_code = next_match(&mut cme_error_elements, Rule::integer)?;
90    let parsed_error_code = parse_integer(error_code)?;
91
92    Ok(Response::CmeError(parsed_error_code))
93}
94
95fn parse_success(success: Pair<'_, Rule>) -> ParseResult<Response, Rule> {
96    let mut success_elements = success.into_inner();
97
98    let optional_extension = next_match(&mut success_elements, Rule::optional_extension)?;
99    let parsed_optional_extension = parse_optional_extension(optional_extension)?;
100
101    let name = next_match(&mut success_elements, Rule::command_name)?;
102    let parsed_name = parse_name(name)?;
103
104    let delimited_arguments = next_match(&mut success_elements, Rule::delimited_arguments)?;
105    let parsed_delimited_arguments =
106        ARGUMENTS_PARSER.parse_delimited_arguments(delimited_arguments)?;
107
108    Ok(Response::Success {
109        name: parsed_name,
110        is_extension: parsed_optional_extension,
111        arguments: parsed_delimited_arguments,
112    })
113}
114
115fn parse_optional_extension(optional_extension: Pair<'_, Rule>) -> ParseResult<bool, Rule> {
116    let extension_str = optional_extension.as_span().as_str();
117
118    match extension_str {
119        "" => Ok(false),
120        "+" => Ok(true),
121        c => Err(ParseError::UnknownExtensionCharacter(c.to_string()).into()),
122    }
123}