mini_async_repl 0.2.1

An async-first REPL
Documentation
use std::net::IpAddr;
use std::path::PathBuf;

use anyhow::{self, Context};
use mini_async_repl::{
    command::{
        lift_validation_err, validate, ArgsError, Command, CommandArgInfo, CommandArgType,
        ExecuteCommand,
    },
    CommandStatus, Repl,
};
use std::future::Future;
use std::pin::Pin;

struct LsCommandHandler {}
impl LsCommandHandler {
    pub fn new() -> Self {
        Self {}
    }
    async fn handle_command(&mut self, dir: PathBuf) -> anyhow::Result<CommandStatus> {
        for entry in dir.read_dir()? {
            println!("{}", entry?.path().to_string_lossy());
        }
        Ok(CommandStatus::Done)
    }
}
impl ExecuteCommand for LsCommandHandler {
    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 dir_buf: PathBuf = args[0].clone().into();
        Box::pin(self.handle_command(dir_buf))
    }
}

struct IpAddrCommandHandler {}
impl IpAddrCommandHandler {
    pub fn new() -> Self {
        Self {}
    }
    async fn handle_command(&mut self, ip: IpAddr) -> anyhow::Result<CommandStatus> {
        println!("{}", ip);
        Ok(CommandStatus::Done)
    }
}
impl ExecuteCommand for IpAddrCommandHandler {
    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 ip = args[0].parse();

        match ip {
            Ok(ip) => Box::pin(self.handle_command(ip)),
            Err(e) => Box::pin(lift_validation_err(Err(ArgsError::WrongArgumentValue {
                argument: args[0].clone(),
                error: e.to_string(),
            }))),
        }
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    #[rustfmt::skip]
    let mut repl = Repl::builder()
        .add("ls", Command::new(
            "List files in a directory",
            vec![CommandArgInfo::new_with_name(CommandArgType::Custom, "dir")],
            Box::new(LsCommandHandler::new()),
        ))
        .add("ipaddr", Command::new(
            "Just parse and print the given IP address".into(),
            vec![CommandArgInfo::new_with_name(CommandArgType::Custom, "ip")],
            Box::new(IpAddrCommandHandler::new()),
        ))
        .build()
        .context("Failed to create repl")?;

    repl.run().await.context("Critical REPL error")
}