at_commands/lowlevel/
arguments.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//! This module contains an AST for arguments for both AT commands and responses.
6//!
7//! The format of of these is not specifed in any one place in the spec, but they are
8//! described thoughout HFP 1.8.
9
10use crate::lowlevel::write_to::WriteTo;
11use std::io;
12
13/// An argument list set off from a command or response by a delimiter such as "=" or ":".
14#[derive(Debug, Clone, PartialEq)]
15pub struct DelimitedArguments {
16    /// A string setting off arguments from the command or response.  This is normally `=`
17    /// or ": ", but could be ">" or absent. This latter is currently only in a variants
18    /// of  the `ATD` command, specified in HFP v1.8 4.19.
19    pub delimiter: Option<String>,
20    /// The actual arguments to the execute commmand.
21    pub arguments: Arguments,
22    /// An optional terminator for the arguments to the command, such as the ";" used to
23    /// terminate ATD commands.
24    pub terminator: Option<String>,
25}
26
27impl WriteTo for DelimitedArguments {
28    fn write_to<W: io::Write>(&self, sink: &mut W) -> io::Result<()> {
29        let DelimitedArguments { delimiter, arguments, terminator } = self;
30        if let Some(string) = delimiter {
31            if string == ":" {
32                // Hack.  The parser ignores whitespace, but the colon in a response must be followed by a space, so special case this.
33                sink.write_all(": ".as_bytes())?;
34            } else {
35                sink.write_all(string.as_bytes())?;
36            }
37        };
38        arguments.write_to(sink)?;
39        if let Some(terminator) = terminator {
40            sink.write_all(terminator.as_bytes())?
41        };
42
43        Ok(())
44    }
45}
46/// The collection of arguments to a given command or response.
47///
48/// AT supports multiple different formats, represented here by the different enum
49/// branches.
50#[derive(Debug, Clone, PartialEq)]
51pub enum Arguments {
52    /// A sequence of multiple arguments lists delimited by parentheses, like, `(1,2)(3,4)(a=1)`
53    ParenthesisDelimitedArgumentLists(Vec<Vec<Argument>>),
54    /// A single argument list delimited by commas, like, `1,2,a=3`
55    ArgumentList(Vec<Argument>),
56}
57
58impl Arguments {
59    fn write_comma_delimited_argument_list<W: io::Write>(
60        &self,
61        arguments: &[Argument],
62        sink: &mut W,
63    ) -> io::Result<()> {
64        if !arguments.is_empty() {
65            for argument in &arguments[0..arguments.len() - 1] {
66                argument.write_to(sink)?;
67                sink.write_all(b",")?;
68            }
69            arguments[arguments.len() - 1].write_to(sink)?;
70        }
71
72        Ok(())
73    }
74
75    fn write_paren_delimited_argument_lists<W: io::Write>(
76        &self,
77        argument_lists: &[Vec<Argument>],
78        sink: &mut W,
79    ) -> io::Result<()> {
80        for arguments in argument_lists {
81            sink.write_all(b"(")?;
82            self.write_comma_delimited_argument_list(arguments, sink)?;
83            sink.write_all(b")")?;
84        }
85
86        Ok(())
87    }
88}
89
90impl WriteTo for Arguments {
91    fn write_to<W: io::Write>(&self, sink: &mut W) -> io::Result<()> {
92        match self {
93            Arguments::ParenthesisDelimitedArgumentLists(argument_lists) => {
94                self.write_paren_delimited_argument_lists(&argument_lists, sink)
95            }
96            Arguments::ArgumentList(argument_list) => {
97                self.write_comma_delimited_argument_list(&argument_list, sink)
98            }
99        }
100    }
101}
102
103/// An individual argument in a list.
104#[derive(Debug, Clone, PartialEq)]
105pub enum Argument {
106    /// A primitive string or int.
107    PrimitiveArgument(String),
108    /// A key-value pair like `a=1`
109    KeyValueArgument { key: String, value: String },
110}
111
112impl Argument {
113    pub fn is_empty(&self) -> bool {
114        match self {
115            Argument::PrimitiveArgument(argument) => argument.is_empty(),
116            Argument::KeyValueArgument { key, value } => key.is_empty() && value.is_empty(),
117        }
118    }
119}
120
121impl WriteTo for Argument {
122    fn write_to<W: io::Write>(&self, sink: &mut W) -> io::Result<()> {
123        match self {
124            Argument::PrimitiveArgument(argument) => sink.write_all(argument.as_bytes())?,
125            Argument::KeyValueArgument { key, value } => {
126                sink.write_all(key.as_bytes())?;
127                sink.write_all(b"=")?;
128                sink.write_all(value.as_bytes())?;
129            }
130        }
131        Ok(())
132    }
133}