Skip to main content

at_parser_rs/
context.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::{Args, AtError, AtResult};
22
23/// Trait that defines the context for AT command execution.
24///
25/// Implement this trait for each AT command your device exposes.
26/// Each method corresponds to one of the four standard AT command forms.
27/// All methods have a default implementation that returns
28/// [`AtError::NotSupported`], so you only need to override the forms your
29/// command actually needs.
30///
31/// The const generic `SIZE` defines the capacity (in bytes) of the
32/// [`Bytes`](osal_rs::utils::Bytes) response buffer returned by each handler.
33/// All handlers registered in the same [`AtParser`](crate::parser::AtParser)
34/// must use the same `SIZE`.
35///
36/// # Example — minimal handler
37///
38/// ```rust,no_run
39/// use at_parser_rs::context::AtContext;
40/// use at_parser_rs::{Args, AtResult, AtError};
41/// use osal_rs::utils::Bytes;
42///
43/// const SIZE: usize = 64;
44///
45/// struct ResetModule;
46///
47/// impl AtContext<SIZE> for ResetModule {
48///     fn exec(&mut self) -> AtResult<'_, SIZE> {
49///         // AT+RST performs a system reset
50///         Ok(Bytes::from_str("OK"))
51///     }
52/// }
53/// ```
54///
55/// # Example — full handler with all forms
56///
57/// ```rust,no_run
58/// use at_parser_rs::context::AtContext;
59/// use at_parser_rs::{Args, AtResult, AtError};
60/// use osal_rs::utils::Bytes;
61///
62/// const SIZE: usize = 64;
63///
64/// struct EchoModule { enabled: bool }
65///
66/// impl AtContext<SIZE> for EchoModule {
67///     fn exec(&mut self) -> AtResult<'_, SIZE> {
68///         let s = if self.enabled { "ECHO: ON" } else { "ECHO: OFF" };
69///         Ok(Bytes::from_str(s))
70///     }
71///     fn query(&mut self) -> AtResult<'_, SIZE> {
72///         Ok(Bytes::from_str(if self.enabled { "1" } else { "0" }))
73///     }
74///     fn test(&mut self) -> AtResult<'_, SIZE> {
75///         Ok(Bytes::from_str("(0,1)"))
76///     }
77///     fn set(&mut self, args: Args) -> AtResult<'_, SIZE> {
78///         let value = args.get(0).ok_or(AtError::InvalidArgs)?;
79///         match value.as_ref() {
80///             "0" => { self.enabled = false; Ok(Bytes::from_str("OK")) }
81///             "1" => { self.enabled = true;  Ok(Bytes::from_str("OK")) }
82///             _ => Err(AtError::InvalidArgs),
83///         }
84///     }
85/// }
86/// ```
87pub trait AtContext<const SIZE: usize> {
88
89    /// Execute command (`AT+CMD`)
90    ///
91    /// Called when the command is invoked without any suffix.
92    /// Use this to implement an action that does not require parameters.
93    ///
94    /// # Returns
95    ///
96    /// * `Ok(Bytes<SIZE>)` — response to send back to the caller
97    /// * `Err(AtError::NotSupported)` — default when not overridden
98    ///
99    /// # Example
100    ///
101    /// ```rust,no_run
102    /// # use at_parser_rs::context::AtContext;
103    /// # use at_parser_rs::{AtResult};
104    /// # use osal_rs::utils::Bytes;
105    /// # const SIZE: usize = 64;
106    /// struct PingModule;
107    ///
108    /// impl AtContext<SIZE> for PingModule {
109    ///     fn exec(&mut self) -> AtResult<'_, SIZE> {
110    ///         Ok(Bytes::from_str("PONG"))
111    ///     }
112    /// }
113    /// // AT+PING  →  "PONG"
114    /// ```
115    fn exec(&mut self) -> AtResult<'_, SIZE> {
116        Err(AtError::NotSupported)
117    }
118
119    /// Query command (`AT+CMD?`)
120    ///
121    /// Called to retrieve the current value or state of the command.
122    ///
123    /// # Returns
124    ///
125    /// * `Ok(Bytes<SIZE>)` — current value/state
126    /// * `Err(AtError::NotSupported)` — default when not overridden
127    ///
128    /// # Example
129    ///
130    /// ```rust,no_run
131    /// # use at_parser_rs::context::AtContext;
132    /// # use at_parser_rs::{AtResult};
133    /// # use osal_rs::utils::Bytes;
134    /// # const SIZE: usize = 64;
135    /// struct VolumeModule { level: u8 }
136    ///
137    /// impl AtContext<SIZE> for VolumeModule {
138    ///     fn query(&mut self) -> AtResult<'_, SIZE> {
139    ///         let mut buf = Bytes::new();
140    ///         buf.format(core::format_args!("{}", self.level));
141    ///         Ok(buf)
142    ///     }
143    /// }
144    /// // AT+VOL?  →  "75"  (if level == 75)
145    /// ```
146    fn query(&mut self) -> AtResult<'_, SIZE> {
147        Err(AtError::NotSupported)
148    }
149    
150    /// Test command (`AT+CMD=?`)
151    ///
152    /// Called to report whether a command is supported or to return the
153    /// valid parameter ranges accepted by [`set`](AtContext::set).
154    ///
155    /// # Returns
156    ///
157    /// * `Ok(Bytes<SIZE>)` — human-readable description of valid parameters
158    /// * `Err(AtError::NotSupported)` — default when not overridden
159    ///
160    /// # Example
161    ///
162    /// ```rust,no_run
163    /// # use at_parser_rs::context::AtContext;
164    /// # use at_parser_rs::{AtResult};
165    /// # use osal_rs::utils::Bytes;
166    /// # const SIZE: usize = 64;
167    /// struct VolumeModule { level: u8 }
168    ///
169    /// impl AtContext<SIZE> for VolumeModule {
170    ///     fn test(&mut self) -> AtResult<'_, SIZE> {
171    ///         Ok(Bytes::from_str("(0-100)"))
172    ///     }
173    /// }
174    /// // AT+VOL=?  →  "(0-100)"
175    /// ```
176    fn test(&mut self) -> AtResult<'_, SIZE> {
177        Err(AtError::NotSupported)
178    }
179
180    /// Set command (`AT+CMD=<args>`)
181    ///
182    /// Called to configure the command with one or more parameters.
183    /// Arguments are accessible via [`Args::get`](crate::Args::get) using a
184    /// 0-based comma-separated index. Quoted arguments are unquoted and
185    /// escape sequences such as `\"` are decoded automatically.
186    ///
187    /// # Arguments
188    ///
189    /// * `args` — parsed argument list; use `args.get(n)` to retrieve the
190    ///   n-th comma-separated token (0-indexed)
191    ///
192    /// # Returns
193    ///
194    /// * `Ok(Bytes<SIZE>)` — confirmation/response
195    /// * `Err(AtError::InvalidArgs)` — when arguments are missing or invalid
196    /// * `Err(AtError::NotSupported)` — default when not overridden
197    ///
198    /// # Example
199    ///
200    /// ```rust,no_run
201    /// # use at_parser_rs::context::AtContext;
202    /// # use at_parser_rs::{Args, AtResult, AtError};
203    /// # use osal_rs::utils::Bytes;
204    /// # const SIZE: usize = 64;
205    /// struct VolumeModule { level: u8 }
206    ///
207    /// impl AtContext<SIZE> for VolumeModule {
208    ///     fn set(&mut self, args: Args) -> AtResult<'_, SIZE> {
209    ///         let val: u8 = args.get(0)
210    ///             .ok_or(AtError::InvalidArgs)?
211    ///             .parse()
212    ///             .map_err(|_| AtError::InvalidArgs)?;
213    ///         if val > 100 { return Err(AtError::InvalidArgs); }
214    ///         self.level = val;
215    ///         Ok(Bytes::from_str("OK"))
216    ///     }
217    /// }
218    /// // AT+VOL=75   →  "OK"      (sets level to 75)
219    /// // AT+VOL=200  →  Err(InvalidArgs)
220    /// // AT+VOL=     →  Err(InvalidArgs)
221    /// ```
222    fn set(&mut self, _args: Args) -> AtResult<'_, SIZE> {
223        Err(AtError::NotSupported)
224    }
225
226}