at_parser_rs/
parser.rs

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