Skip to main content

at_modules

Macro at_modules 

Source
macro_rules! at_modules {
    (
        $size:expr;
        $( $name: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" => HANDLER1,
    "AT+CMD2" => HANDLER2,
}
  • SIZEconst usize that defines the response buffer capacity (must match the capacity used by AtParser and every AtContext impl).
  • "AT+CMDn" — the AT command string the parser will match against.
  • HANDLERn — 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};
use osal_rs::utils::Bytes;

const SIZE: usize = 64;

struct EchoModule { echo: bool }
impl AtContext<SIZE> for EchoModule {
    fn query(&mut self) -> AtResult<SIZE> {
        Ok(Bytes::from_str(if self.echo { "1" } else { "0" }))
    }
    fn set(&mut self, args: Args) -> AtResult<SIZE> {
        let value = args.get(0).ok_or(AtError::InvalidArgs)?;
        match value.as_ref() {
            "0" => { self.echo = false; Ok(Bytes::from_str("OK")) }
            "1" => { self.echo = true;  Ok(Bytes::from_str("OK")) }
            _ => Err(AtError::InvalidArgs),
        }
    }
}

struct ResetModule;
impl AtContext<SIZE> for ResetModule {
    fn exec(&mut self) -> AtResult<SIZE> { Ok(Bytes::from_str("OK")) }
}

static mut ECHO:  EchoModule  = EchoModule { echo: false };
static mut RESET: ResetModule = ResetModule;

at_modules! {
    SIZE;
    "AT+ECHO" => ECHO,
    "AT+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};
use osal_rs::utils::Bytes;

const SIZE: usize = 32;

struct PingModule;
impl AtContext<SIZE> for PingModule {
    fn exec(&mut self) -> AtResult<SIZE> { Ok(Bytes::from_str("PONG")) }
}

static mut PING: PingModule = PingModule;

at_modules! {
    SIZE;
    "AT+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, &mut dyn AtContext<SIZE>)] = &mut [
    ("AT+ECHO", &mut echo),
    ("AT+RST",  &mut reset),
];
parser.set_commands(commands);