rustscript-embedded 0.3.3

Embedded RustScript VMBC runtime with a no_std host-function ABI
use std::io::{self, Write};
use std::time::Duration;

use rustscript_embedded::{
    SerialReplSession, SerialReplTransport, format_repl_value, is_repl_input_complete,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut args = std::env::args().skip(1);
    let port = args
        .next()
        .ok_or("usage: rustscript-serial-repl <port> [baud]")?;
    let baud = args
        .next()
        .map(|value| value.parse::<u32>())
        .transpose()?
        .unwrap_or(115_200);
    if args.next().is_some() {
        return Err("usage: rustscript-serial-repl <port> [baud]".into());
    }

    let serial = serialport::new(&port, baud)
        .timeout(Duration::from_secs(10))
        .open()?;
    serial.clear(serialport::ClearBuffer::Input)?;
    let mut transport = SerialReplTransport::new(serial);
    let mut session = SerialReplSession::new();
    let mut pending = String::new();
    let stdin = io::stdin();

    println!("RustScript serial REPL on {port} ({baud} baud)");
    println!("commands: .help, .cancel, .clear, .quit");
    loop {
        print!("{}", if pending.is_empty() { "rss> " } else { "...> " });
        io::stdout().flush()?;
        let mut line = String::new();
        if stdin.read_line(&mut line)? == 0 {
            println!();
            break;
        }
        let command = line.trim();
        match command {
            ".quit" | ".exit" => break,
            ".help" if pending.is_empty() => {
                println!(".cancel clears multiline input; .clear clears saved locals; .quit exits");
                continue;
            }
            ".cancel" => {
                pending.clear();
                continue;
            }
            ".clear" if pending.is_empty() => {
                session.clear();
                println!("session cleared");
                continue;
            }
            _ if command.starts_with('.') && pending.is_empty() => {
                println!("unknown command: {command}");
                continue;
            }
            _ => {}
        }
        if line.trim().is_empty() && pending.is_empty() {
            continue;
        }
        pending.push_str(&line);
        if !is_repl_input_complete(&pending) {
            continue;
        }
        let snippet = std::mem::take(&mut pending);
        let result = session.eval(&snippet, &mut transport);
        let device_output = transport.take_device_output();
        if !device_output.is_empty() {
            io::stdout().write_all(&device_output)?;
            if !device_output.ends_with(b"\n") {
                println!();
            }
        }
        match result {
            Ok(Some(value)) => println!("=> {}", format_repl_value(&value)),
            Ok(None) => {}
            Err(error) => eprintln!("error: {error}"),
        }
    }
    Ok(())
}