huawei_modem/
at.rs

1//! Types for dealing with AT commands and replies.
2use crate::error_codes::CmsError;
3use std::fmt;
4use crate::errors::{HuaweiError, HuaweiResult};
5/// An AT result code, which indicates the completion of a command.
6#[derive(Fail, Debug, Clone, PartialEq, Eq, is_enum_variant)]
7pub enum AtResultCode {
8    /// Command executed without failure.
9    #[fail(display = "A command is executed, and there is no error.")]
10    Ok,
11    /// Connection established.
12    #[fail(display = "A connection is established.")]
13    Connect,
14    /// Incoming call.
15    #[fail(display = "An incoming call is originated.")]
16    Ring,
17    /// Connection terminated.
18    #[fail(display = "A connection is terminated.")]
19    NoCarrier,
20    /// Generic error (rather unhelpful).
21    #[fail(display = "A generic error occurred.")]
22    Error,
23    /// CME error (= generic error), with annoyingly opaque error code (will be fixed).
24    ///
25    /// There is a list of CME errors that I should really get around to
26    /// making into an `enum`. However, that's annoying, so I haven't done
27    /// it yet.
28    #[fail(display = "An error occurred: code {}", _0)]
29    CmeError(u32),
30    /// Typed CMS error (= SMS-related error) that uses one of the
31    /// available error codes.
32    #[fail(display = "An SMS-related error occurred: {}", _0)]
33    CmsError(#[cause] CmsError),
34    /// CMS error given as string, because of modem configuration.
35    ///
36    /// There's probably some way to get modems to report errors as a numeric
37    /// error code, so you can make use of the `enum`. However, I don't know
38    /// of one.
39    #[fail(display = "An unknown SMS-related error occurred: {}", _0)]
40    CmsErrorString(String),
41    /// Unknown CMS error code.
42    #[fail(display = "An unknown SMS-related error occurred: code {}", _0)]
43    CmsErrorUnknown(u32),
44    /// No dialtone.
45    #[fail(display = "There is no dialtone.")]
46    NoDialtone,
47    /// Recipient busy.
48    #[fail(display = "Recipient is busy.")]
49    Busy,
50    /// No answer.
51    #[fail(display = "No reply (timeout occurred).")]
52    NoAnswer,
53    /// Command not supported.
54    #[fail(display = "Command not supported.")]
55    CommandNotSupported,
56    /// Too many parameters.
57    #[fail(display = "Too many parameters.")]
58    TooManyParameters
59}
60/// Any of a set of types used in AT commands.
61#[derive(Debug, Clone, PartialEq, Eq, is_enum_variant)]
62pub enum AtValue {
63    /// A string-type value - text surrounded by "quotation marks".
64    String(String),
65    /// An integer.
66    Integer(u32),
67    /// A range of integers.
68    Range((u32, u32)),
69    /// Some untyped value - usually 'bareword' strings, i.e. strings that aren't
70    /// surrounded in "quotation marks".
71    Unknown(String),
72    /// An empty value, corresponding to nothing at all.
73    Empty,
74    /// A bracketed array.
75    BracketedArray(Vec<AtValue>),
76    /// A non-bracketed array.
77    Array(Vec<AtValue>)
78}
79macro_rules! at_value_impl {
80    ($atv:ident, $($var:ident, $refmeth:ident, $mutmeth:ident, $asmeth:ident, $ty:ty),*) => {
81        /// This `impl` block provides methods to extract various types
82        /// out of an `AtValue`. If the value is not of the desired type,
83        /// `HuaweiError::TypeMismatch` is returned.
84        ///
85        /// - `as_x` methods take `self`, and return either the type or an error.
86        /// - `get_x` methods take `&self`, and return a `&` reference.
87        /// - `get_x_mut` methods take `&mut self`, and return a `&mut` reference.
88        impl $atv {
89            $(
90                pub fn $refmeth(&self) -> HuaweiResult<&$ty> {
91                    if let $atv::$var(ref i) = *self {
92                        Ok(i)
93                    }
94                    else {
95                        Err(HuaweiError::TypeMismatch)
96                    }
97                }
98                pub fn $mutmeth(&mut self) -> HuaweiResult<&mut $ty> {
99                    if let $atv::$var(ref mut i) = *self {
100                        Ok(i)
101                    }
102                    else {
103                        Err(HuaweiError::TypeMismatch)
104                    }
105                }
106                pub fn $asmeth(self) -> HuaweiResult<$ty> {
107                    if let $atv::$var(i) = self {
108                        Ok(i)
109                    }
110                    else {
111                        Err(HuaweiError::TypeMismatch)
112                    }
113                }
114             )*
115        }
116    }
117}
118at_value_impl!(AtValue,
119               String, get_string, get_string_mut, as_string, String,
120               Integer, get_integer, get_integer_mut, as_integer, u32,
121               Range, get_range, get_range_mut, as_range, (u32, u32),
122               Unknown, get_unknown, get_unknown_mut, as_unknown, String,
123               BracketedArray, get_bracketed_array, get_bracketed_array_mut, as_bracketed_array, Vec<AtValue>,
124               Array, get_array, get_array_mut, as_array, Vec<AtValue>);
125/// Writes the `AtValue` out, as it would appear on the command line.
126///
127/// This `impl` is directly used for formatting `AtValue`s when making
128/// AT commands.
129impl fmt::Display for AtValue {
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        use self::AtValue::*;
132        match *self {
133            String(ref st) => write!(f, "\"{}\"", st)?,
134            Integer(i) => write!(f, "{}", i)?,
135            Range((a, b)) => write!(f, "{}-{}", a, b)?,
136            Unknown(ref st) => write!(f, "{}", st)?,
137            Empty => {},
138            BracketedArray(ref val) => {
139                write!(f, "(")?;
140                for (i, val) in val.iter().enumerate() {
141                    let c = if i == 0 { "" } else { "," };
142                    write!(f, "{}{}", c, val)?;
143                }
144                write!(f, ")")?;
145            },
146            Array(ref val) => {
147                for (i, val) in val.iter().enumerate() {
148                    let c = if i == 0 { "" } else { "," };
149                    write!(f, "{}{}", c, val)?;
150                }
151            }
152        }
153        Ok(())
154    }
155}
156/// One of possibly many response lines to an AT command.
157///
158/// One `AtResponse` always corresponds to one line of text.
159#[derive(Debug, Clone, PartialEq, Eq, is_enum_variant)]
160pub enum AtResponse {
161    /// An information response issued as a result of a command.
162    ///
163    /// Corresponds to '<param>: <response>'.
164    InformationResponse {
165        param: String,
166        response: AtValue
167    },
168    /// An AT result code, indicating the completion of a command.
169    ResultCode(AtResultCode),
170    /// Some other unknown response.
171    Unknown(String)
172}
173/// The complete set of responses to an issued AT command.
174#[derive(Debug, Clone)]
175pub struct AtResponsePacket {
176    /// The various `AtResponses` issued.
177    ///
178    /// Note that this will only contain 'expected' `InformationResponse`s,
179    /// as well as any `Unknown` responses. 'Expected' values are values
180    /// that were expected as a result of the command issued - for more
181    /// information, see the `AtCommand` documentation.
182    pub responses: Vec<AtResponse>,
183    /// The final result code for this command.
184    pub status: AtResultCode
185}
186impl AtResponsePacket {
187    /// Extracts the value of an `InformationResponse` that has a given `resp`
188    /// as its `param`, if such a response exists.
189    ///
190    /// Also invokes `self.assert_ok()?`, to verify that the response was successful.
191    pub fn extract_named_response_opt(&self, resp: &str) -> HuaweiResult<Option<&AtValue>> {
192        self.assert_ok()?;
193        for r in self.responses.iter() {
194            if let AtResponse::InformationResponse { ref param, ref response } = *r {
195                if resp == param {
196                    return Ok(Some(response));
197                }
198            }
199        }
200        Ok(None)
201    }
202    /// Like `extract_named_response_opt`, but fails with a `HuaweiError::ExpectedResponse` if the
203    /// named response doesn't actually exist.
204    pub fn extract_named_response(&self, resp: &str) -> HuaweiResult<&AtValue> {
205        match self.extract_named_response_opt(resp)? {
206            Some(val) => Ok(val),
207            None => Err(HuaweiError::ExpectedResponse(resp.into()))
208        }
209    }
210    /// Returns `HuaweiError::AtError(self.status.clone())` if the status code was not `Ok`.
211    pub fn assert_ok(&self) -> HuaweiResult<()> {
212        if self.status.is_ok() {
213            Ok(())
214        }
215        else {
216            Err(HuaweiError::AtError(self.status.clone()))
217        }
218    }
219}
220impl AtCommand {
221    /// Get the set of 'expected' `InformationResponse`s for this command.
222    ///
223    /// This is used by the library to filter out URCs (Unsolicited Response Codes) - basically,
224    /// commands only get `InformationResponse`s that match their `expected()` array, so we can
225    /// filter all of the other responses out and assume that they're URCs.
226    ///
227    /// - For `Equals`, `Read`, and `Test`, this is the value of `vec![param]`.
228    /// - For `Execute` and `Basic`, this is the value of `vec![command]`.
229    /// - For `Text`, this is the value of `expected`.
230    pub fn expected(&self) -> Vec<String> {
231        match *self {
232            AtCommand::Equals { ref param, .. } => vec![param.clone()],
233            AtCommand::Execute { ref command } => vec![command.clone()],
234            AtCommand::Read { ref param } => vec![param.clone()],
235            AtCommand::Test { ref param } => vec![param.clone()],
236            AtCommand::Basic { ref command, .. } => vec![command.clone()],
237            AtCommand::Text { ref expected, .. } => expected.clone(),
238        }
239    }
240}
241/// An AT command.
242#[derive(Debug, Clone, PartialEq, Eq, is_enum_variant)]
243pub enum AtCommand {
244    /// Either execute a non-basic command named `param` with `value` as
245    /// argument, or set the current value of `param` to `value`.
246    ///
247    /// Corresponds to `AT<param>=<value>`.
248    Equals {
249        param: String,
250        value: AtValue,
251    },
252    /// Execute a non-basic command, with the name of `command`.
253    ///
254    /// Corresponds to `AT<command>`.
255    Execute {
256        command: String
257    },
258    /// Read the current value of `param`.
259    ///
260    /// Corresponds to `AT<param>?`.
261    Read {
262        param: String
263    },
264    /// Return the available value range of `param`.
265    ///
266    /// Corresponds to `AT<param>=?'.
267    Test {
268        param: String
269    },
270    /// Execute a basic command, where `command` indicates a single letter (A-Z)
271    /// or the & symbol and a single letter, with an optional number parameter.
272    ///
273    /// Corresponds to `AT<command>[<number>]`.
274    Basic {
275        command: String,
276        number: Option<usize>
277    },
278    /// Just send some raw text.
279    Text {
280        text: String,
281        /// The set of 'expected' `InformationResponse`s to this command.
282        expected: Vec<String>
283    }
284}
285/// Writes the `AtCommand` out, as it would appear on the command line.
286impl fmt::Display for AtCommand {
287    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
288        use self::AtCommand::*;
289        match *self {
290            Equals { ref param, ref value } => write!(f, "AT{}={}", param, value)?,
291            Execute { ref command } => write!(f, "AT{}", command)?,
292            Read { ref param } => write!(f, "AT{}?", param)?,
293            Test { ref param } => write!(f, "AT{}=?", param)?,
294            Basic { ref command, ref number } => {
295                write!(f, "AT{}", command)?;
296                if let Some(n) = *number {
297                    write!(f, "{}", n)?;
298                }
299            },
300            Text { ref text, .. } => write!(f, "{}", text)?
301        }
302        Ok(())
303    }
304}