macro_rules! at_modules {
(
$size:expr;
$( ($name:expr, $at_resp:expr) => $module:ident ),* $(,)?
) => { ... };
}Expand description
Declares a static COMMANDS table mapping AT command strings to their handlers.
This macro expands into a static COMMANDS binding of type
&[(&'static str, &mut dyn AtContext<SIZE>)], which can then be passed to
AtParser::set_commands.
§Syntax
ⓘ
at_modules! {
SIZE;
("AT+CMD1", "+CMD1: ") => HANDLER1,
("AT+CMD2", "+CMD2: ") => HANDLER2,
}SIZE—const usizethat defines the response buffer capacity (must match the capacity used byAtParserand everyAtContextimpl)."AT+CMD"— the AT command string the parser will match against the input."+CMD: "— the AT response prefix forwarded to every handler method.HANDLER— astatic mutvariable that implementsAtContext<SIZE>.
§Safety
The macro uses an unsafe block internally to obtain &mut references to
static mut items. It is the caller’s responsibility to ensure:
- Single-threaded access only — do not call this in a multi-threaded or RTOS context without external synchronisation.
- One call site — the generated
COMMANDSsymbol isstatic; defining it more than once in the same scope will cause a compile error.
§Limitations
- All handlers must implement
AtContextwith the sameSIZEconstant. - The generated symbol is always named
COMMANDS; rename it after expansion if you need multiple tables.
§Example — basic usage
use at_parser_rs::at_modules;
use at_parser_rs::context::AtContext;
use at_parser_rs::{Args, AtResult, AtError, at_response};
const SIZE: usize = 64;
struct EchoModule { echo: bool }
impl AtContext<SIZE> for EchoModule {
fn query(&mut self, at_response: &'static str) -> AtResult<SIZE> {
Ok(at_response!(SIZE, at_response; if self.echo { 1u8 } else { 0u8 }))
}
fn set(&mut self, at_response: &'static str, args: Args) -> AtResult<SIZE> {
let value = args.get(0).ok_or((at_response, AtError::InvalidArgs))?;
match value.as_ref() {
"0" => { self.echo = false; Ok(at_response!(SIZE, at_response; "OK")) }
"1" => { self.echo = true; Ok(at_response!(SIZE, at_response; "OK")) }
_ => Err((at_response, AtError::InvalidArgs)),
}
}
}
struct ResetModule;
impl AtContext<SIZE> for ResetModule {
fn exec(&mut self, at_response: &'static str) -> AtResult<SIZE> {
Ok(at_response!(SIZE, at_response; "OK"))
}
}
static mut ECHO: EchoModule = EchoModule { echo: false };
static mut RESET: ResetModule = ResetModule;
at_modules! {
SIZE;
("AT+ECHO", "+ECHO: ") => ECHO,
("AT+RST", "+RST: ") => RESET,
}
// COMMANDS is now available in scope:
// parser.set_commands(COMMANDS);§Example — single handler
use at_parser_rs::at_modules;
use at_parser_rs::context::AtContext;
use at_parser_rs::{AtResult, at_response};
const SIZE: usize = 32;
struct PingModule;
impl AtContext<SIZE> for PingModule {
fn exec(&mut self, at_response: &'static str) -> AtResult<SIZE> {
Ok(at_response!(SIZE, at_response; "PONG"))
}
}
static mut PING: PingModule = PingModule;
at_modules! {
SIZE;
("AT+PING", "+PING: ") => PING,
}§Recommended alternative
For multi-type handler tables or when static mut is undesirable, prefer the
explicit slice approach — it requires no unsafe at the call site and allows
mixing handler types via trait objects:
use at_parser_rs::context::AtContext;
const SIZE: usize = 64;
let mut echo = EchoModule { echo: false };
let mut reset = ResetModule;
let commands: &mut [(&str, &str, &mut dyn AtContext<SIZE>)] = &mut [
("AT+ECHO", "+ECHO: ", &mut echo),
("AT+RST", "+RST: ", &mut reset),
];
parser.set_commands(commands);