Minimal no_std building blocks for serial REPL interfaces on MCUs.
This crate does not provide I/O, command dispatch, or editing beyond single-byte backspace handling. It focuses on the byte-to-line and line-to-parts steps that are awkward to rewrite in every embedded project.
# use core::fmt;
# traitSerial: fmt::Write{
# fntry_read(&mutself) -> Option<u8>;
# fnwrite(&mutself,byte:u8);
# }use no_std_repl::{LineBuffer,Outcome,Parts};fnrepl_loop(serial:&mutimplSerial){letmut line = LineBuffer::<128>::new();loop{write!(serial,"> ").ok();// Read until newlineloop{ifletSome(byte) = serial.try_read(){
serial.write(byte);// echomatch line.push(byte){Outcome::Continue => {}Outcome::LineComplete => {writeln!(serial).ok();break;}Outcome::BufferFull => {writeln!(serial,"\r\nline too long").ok();
line.clear();break;}}}}let s = match line.as_str(){Some(s) => s,None => {writeln!(serial,"invalid input").ok();
line.clear();continue;}};// Pattern match on command partslet parts = Parts::<8>::parse(s);if parts.is_truncated(){writeln!(serial,"too many arguments").ok();
line.clear();continue;}match parts.as_slice(){["ping"] => {writeln!(serial,"pong").ok();}["ping", n] => {ifletOk(v) = n.parse::<u32>(){writeln!(serial,"ping {v}").ok();}else{writeln!(serial,"bad number: {n}").ok();}}["set", key, value] => {writeln!(serial,"setting {key} = {value}").ok();}["help"] => {writeln!(serial,"commands: ping, set <key> <value>, help").ok();}[] => {}
other => {writeln!(serial,"unknown: {other:?}").ok();}}
line.clear();}}
# fnmain(){}Three types, zero dependencies, zero allocations:
LineBuffer<N>- Accumulates bytes until newline, handles backspace and CR+LF coalescingParts<'a, N>- Parses a line into parts for slice pattern matchingOutcome- Push result:Continue,LineComplete, orBufferFull
The pattern matching syntax (["cmd", arg1, arg2]) is standard Rust slice patterns.
- Line endings:
\r,\n, and\r\nall terminate the line. The terminator is not stored in the buffer. - Backspace: both
0x08and0x7Fremove one buffered byte if present. - UTF-8:
LineBuffer::as_str()returnsNonefor invalid UTF-8, butLineBuffer::as_bytes()always exposes the raw bytes. - Part limit:
Parts::<N>::parse()keeps at mostNwhitespace-separated parts. If additional parts were present,Parts::is_truncated()returnstrue.
Run the host-side demo:
cargo run --example host_repl
See examples/host_repl.rs for a complete byte-by-byte REPL loop.