Skip to main content

at_parser_rs/
parser.rs

1/***************************************************************************
2 *
3 * AT Command Parser
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20 
21use crate::context::AtContext;
22use crate::{AtError, AtResult, Args};
23
24/*
25AT Command Forms:
26- AT+CMD     (execution)
27- AT+CMD?    (query)
28- AT+CMD=?   (test)
29- AT+CMD=... (set with arguments)
30 */
31
32/// Represents the different forms an AT command can take
33enum AtForm<'a> {
34    /// Execute command without parameters (AT+CMD)
35    Exec,
36    /// Query the current state (AT+CMD?)
37    Query,
38    /// Test command availability or get valid ranges (AT+CMD=?)
39    Test,
40    /// Set command with arguments (AT+CMD=args)
41    Set(Args<'a>),
42}
43
44/// The main AT command parser
45/// Generic over T which must implement AtContext trait
46pub struct AtParser<'a, T>
47where
48    T: AtContext {
49    /// Array of registered commands with their name and handler
50    pub commands: &'a mut [(&'static str, &'a mut T)],
51}
52
53impl<'a, T> AtParser<'a, T>
54where
55    T: AtContext {
56
57    /// Create a new empty parser
58    pub fn new() -> Self {
59        Self { commands: & mut [] }
60    }
61
62    /// Register commands that this parser will handle
63    pub fn set_commands(&mut self, commands: &'a mut [(&'static str, &'a mut T)]) {
64        self.commands = commands;
65    }
66
67    /// Parse and execute an AT command string
68    /// 
69    /// # Arguments
70    /// * `input` - The raw AT command string (e.g., "AT+CMD?")
71    /// 
72    /// # Returns
73    /// * `Ok(&str)` - Success response from the command handler
74    /// * `Err(AtError)` - Error if parsing fails or command is not found
75    pub fn execute(&mut self, input: &str) -> AtResult<'static> {
76        let input = input.trim();
77        let (name, form) = parse(input)?;
78
79        // Find the command handler
80        let (_, module) = self.commands
81            .iter_mut()
82            .find(|(n, _)| *n == name)
83            .ok_or(AtError::UnknownCommand)?;
84
85        // Dispatch to the appropriate handler method
86        match form {
87            AtForm::Exec => module.exec(),
88            AtForm::Query => module.query(),
89            AtForm::Test => module.test(),
90            AtForm::Set(args) => module.set(args),
91        }
92    }
93}
94
95/// Parse an AT command string into its name and form
96/// 
97/// # Arguments
98/// * `input` - The command string to parse
99/// 
100/// # Returns
101/// A tuple of (command_name, command_form)
102fn parse<'a>(input: &'a str) -> Result<(&'a str, AtForm<'a>), AtError> {
103    let input = input.trim();
104
105    // Check suffixes to determine command form
106    if let Some(cmd) = input.strip_suffix("=?") {
107        Ok((cmd, AtForm::Test))
108    } else if let Some(cmd) = input.strip_suffix('?') {
109        Ok((cmd, AtForm::Query))
110    } else if let Some((cmd, args)) = input.split_once('=') {
111        Ok((cmd, AtForm::Set(Args { raw: args })))
112    } else {
113        Ok((input, AtForm::Exec))
114    }
115}