mini_async_repl 0.2.1

An async-first REPL
Documentation
use anyhow::{self, Context};
use mini_async_repl::{
    command::{
        lift_validation_err, validate, Command, CommandArgInfo, CommandArgType, ExecuteCommand,
    },
    CommandStatus, Repl,
};
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;

struct CountCommandHandler {}
impl CountCommandHandler {
    pub fn new() -> Self {
        Self {}
    }
    async fn handle_command(&mut self, x: i32, y: i32) -> anyhow::Result<CommandStatus> {
        for i in x..=y {
            print!(" {}", i);
        }
        println!();
        Ok(CommandStatus::Done)
    }
}
impl ExecuteCommand for CountCommandHandler {
    fn execute(
        &mut self,
        args: Vec<String>,
        args_info: Vec<CommandArgInfo>,
    ) -> Pin<Box<dyn Future<Output = anyhow::Result<CommandStatus>> + '_>> {
        let valid = validate(args.clone(), args_info.clone());
        if let Err(e) = valid {
            return Box::pin(lift_validation_err(Err(e)));
        }

        let x = args[0].parse::<i32>();
        let y = args[1].parse::<i32>();

        match (x, y) {
            (Ok(x), Ok(y)) => Box::pin(self.handle_command(x, y)),
            _ => panic!("Unreachable, validator should have covered this"),
        }
    }
}

struct SayCommandHandler {}
impl SayCommandHandler {
    pub fn new() -> Self {
        Self {}
    }
    async fn handle_command(&mut self, x: f32) -> anyhow::Result<CommandStatus> {
        println!("x is equal to {}", x);
        Ok(CommandStatus::Done)
    }
}
impl ExecuteCommand for SayCommandHandler {
    fn execute(
        &mut self,
        args: Vec<String>,
        args_info: Vec<CommandArgInfo>,
    ) -> Pin<Box<dyn Future<Output = anyhow::Result<CommandStatus>> + '_>> {
        let valid = validate(args.clone(), args_info.clone());
        if let Err(e) = valid {
            return Box::pin(lift_validation_err(Err(e)));
        }

        let x = args[0].parse::<f32>();
        match x {
            Ok(x) => Box::pin(self.handle_command(x)),
            _ => panic!("Unreachable, validator should have covered this"),
        }
    }
}

struct OutXCommandHandler {
    outside_x: Rc<RefCell<String>>,
}
impl OutXCommandHandler {
    pub fn new(outside_x: Rc<RefCell<String>>) -> Self {
        Self { outside_x }
    }
    async fn handle_command(&mut self) -> anyhow::Result<CommandStatus> {
        let mut x = self.outside_x.borrow_mut();
        *x += "x";
        println!("{}", x);
        Ok(CommandStatus::Done)
    }
}
impl ExecuteCommand for OutXCommandHandler {
    fn execute(
        &mut self,
        args: Vec<String>,
        args_info: Vec<CommandArgInfo>,
    ) -> Pin<Box<dyn Future<Output = anyhow::Result<CommandStatus>> + '_>> {
        let valid = validate(args.clone(), args_info.clone());
        if let Err(e) = valid {
            return Box::pin(lift_validation_err(Err(e)));
        }
        Box::pin(self.handle_command())
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let outside_x = Rc::new(RefCell::new(String::from("Out x")));

    #[rustfmt::skip]
    let mut repl = Repl::builder()
        .description("Example REPL")
        .prompt("=> ")
        .text_width(60 as usize)
        .add("count", Command::new(
        	"Count from X to Y",
        	vec![
        		CommandArgInfo::new_with_name(CommandArgType::I32, "X"),
        		CommandArgInfo::new_with_name(CommandArgType::I32, "Y"),
        	],
        	Box::new(CountCommandHandler::new()),
        ))
        .add("say", Command::new(
        	"Say X",
        	vec![CommandArgInfo::new_with_name(CommandArgType::F32, "X")],
        	Box::new(SayCommandHandler::new()),
        ))
        .add("outx", Command::new(
        	"Use mutably outside var x. This command has a really long description so we need to wrap it somehow, it is interesting how actually the wrapping will be performed.",
        	vec![],
        	Box::new(OutXCommandHandler::new(outside_x.clone())),
        ))
        .build().context("Failed to create repl")?;

    repl.run().await.context("Critical REPL error")?;

    Ok(())
}