Skip to main content

at_modules

Macro at_modules 

Source
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,
}
  • SIZEconst usize that defines the response buffer capacity (must match the capacity used by AtParser and every AtContext impl).
  • "AT+CMD" — the AT command string the parser will match against the input.
  • "+CMD: " — the AT response prefix forwarded to every handler method.
  • HANDLER — a static mut variable that implements AtContext<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 COMMANDS symbol is static; defining it more than once in the same scope will cause a compile error.

§Limitations

  • All handlers must implement AtContext with the same SIZE constant.
  • 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,
}

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);