at-parser-rs 0.3.1

A flexible AT command parser for embedded systems and communication devices with no_std support
Documentation
/***************************************************************************
 *
 * AT Command Parser
 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 *
 ***************************************************************************/

//! Example: AT command table with UART and device configuration handling
//!
//! **Note**: This is a pattern demonstration example showing how to implement
//! configuration commands in no_std/embedded environments. It illustrates
//! the `AtContext` trait implementation for device-specific commands.
//!
//! In a real embedded application, replace the dummy UART implementation
//! with your platform's actual hardware abstraction layer (HAL).

#![allow(dead_code)]
#![no_std]
#![no_main]

extern crate at_parser_rs;

use at_parser_rs::{Args, AtError, AtResult, Bytes};
use at_parser_rs::context::AtContext;

const SIZE: usize = 64;

// UART struct with AtContext implementation for UARTSEND command
struct DummyUart {
    baudrate: u32,
    mode: u8,
}

impl DummyUart {
    const fn new() -> Self {
        Self { baudrate: 9600, mode: 0 }
    }
    
    fn write(&self, data: &str) {
        // In real embedded, this would send data over UART
        let _ = data;
    }
}

impl AtContext<SIZE> for DummyUart {
    fn exec(&self) -> AtResult<'_, SIZE> {
        // Not supported for UARTSEND
        Err(AtError::NotSupported)
    }
    fn set(&mut self, args: Args) -> AtResult<'_, SIZE> {
        if let Some(data) = args.get(0) {
            self.write(data);
            Ok(Bytes::from_str("SENT"))
        } else {
            Err(AtError::InvalidArgs)
        }
    }
}

// Device configuration context
struct ConfigContext;

impl AtContext<SIZE> for ConfigContext {
    fn exec(&self) -> AtResult<'_, SIZE> {
        // Not supported for SETCFG
        Err(AtError::NotSupported)
    }
    
    fn query(&mut self) -> AtResult<'_, SIZE> {
        // Return current configuration as a static string
        // In real implementation, format the values dynamically
        Ok(Bytes::from_str("115200,1"))
    }
    
    fn test(&mut self) -> AtResult<'_, SIZE> {
        // Return supported configuration options
        Ok(Bytes::from_str("+SETCFG: (baudrate),(mode)"))
    }
    
    fn set(&mut self, args: Args) -> AtResult<'_, SIZE> {
        let baud = args.get(0).and_then(|s| s.parse::<u32>().ok());
        let mode = args.get(1).and_then(|s| s.parse::<u8>().ok());
        match (baud, mode) {
            (Some(b), Some(m)) => unsafe {
                // Configure UART
                UART.baudrate = b;
                UART.mode = m;
                Ok(Bytes::from_str("CONFIGURED"))
            },
            _ => Err(AtError::InvalidArgs),
        }
    }
}

static mut UART: DummyUart = DummyUart { baudrate: 9600, mode: 0 };
static mut CONFIG_CTX: ConfigContext = ConfigContext;

// Example using AtParser
fn example_with_parser() -> Result<&'static str, AtError<'static>> {
    // Simplified example for no_std
    // In real embedded code, you would properly initialize the parser
    Ok("OK")
}

// Example using the COMMANDS table generated by at_modules macro
fn example_with_commands_macro() -> Result<&'static str, AtError<'static>> {
    // Simple command parsing without the full parser
    // In a real embedded system, you would use the parser properly
    Ok("OK")
}

// Mock main for compilation (in real embedded code, this would be in your firmware)
#[unsafe(no_mangle)]
pub extern "C" fn main() -> ! {
    // Example usage - in embedded this would be called from your main loop
    let _result = example_with_commands_macro();
    loop {}
}