Skip to content

Commit

Permalink
feat: support system shell command execute in REPL environment
Browse files Browse the repository at this point in the history
  • Loading branch information
D-lyw committed Jul 29, 2024
1 parent 7b31e36 commit b643b47
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 5 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"rust-analyzer.linkedProjects": [
"./algorithm/Cargo.toml",
],
// "rust-analyzer.procMacro.enable": true,
"sqltools.connections": [
{
"pgOptions": {
Expand Down
47 changes: 47 additions & 0 deletions project/repl/examples/no_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Example using Repl without Context (or, more precisely, a Context of ())
use std::collections::HashMap;

use clap::{command, Parser, Subcommand};
use reedline_repl_rs::clap::ArgMatches;
use reedline_repl_rs::{CallBackMap, Repl, Result};

#[derive(Parser, Debug)]
#[command(name = "MyApp", version = "v0.1.0", about = "My very cool List")]
pub struct MyApp {
#[command(subcommand)]
pub command: Commands,
}

#[derive(Debug, Subcommand)]
pub enum Commands {
/// Add two numbers together
Add { first: i32, second: i32 },
/// Greetings!
Hello { who: String },
}

/// Add two numbers. Have to make this generic to be able to pass a Context of type ()
fn add<T>(args: ArgMatches, _context: &mut T) -> Result<Option<String>> {
let first = args.get_one::<i32>("first").unwrap();
let second = args.get_one::<i32>("second").unwrap();

Ok(Some((first + second).to_string()))
}

/// Write "Hello"
fn hello<T>(args: ArgMatches, _context: &mut T) -> Result<Option<String>> {
Ok(Some(format!(
"Hello, {}",
args.get_one::<String>("who").unwrap()
)))
}

fn main() -> Result<()> {
let mut callbacks: CallBackMap<(), reedline_repl_rs::Error> = HashMap::new();
callbacks.insert("add".to_string(), add);
callbacks.insert("hello".to_string(), hello);

let mut repl = Repl::new(()).with_derived::<MyApp>(callbacks);

repl.run()
}
12 changes: 12 additions & 0 deletions project/repl/src/cli/exit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use std::process;

use clap::ArgMatches;
use super::ReplCommandResult;

pub fn exit<T>(_: ArgMatches, _context: &mut T) -> ReplCommandResult {
println!("Bye");
process::exit(0);

#[allow(unreachable_code)]
Ok(None)
}
32 changes: 32 additions & 0 deletions project/repl/src/cli/ls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::process;

use clap::ArgMatches;

use super::ReplCommandResult;

pub fn ls<T>(args: ArgMatches, _context: &mut T) -> ReplCommandResult {
let mut command_args: Vec<String> = vec![];
let show_detail = args.get_one::<bool>("list_detail");
if let Some(true) = show_detail {
command_args.push(format!("-l"));
}
let all_files = args.get_one::<bool>("all_files");
if let Some(true) = all_files {
command_args.push(format!("-a"));
}
let output = process::Command::new("ls")
.args(command_args)
.output()
.expect("Command execute failed");

if output.status.success() {
let out_str = output.stdout;
Ok(Some(
String::from_utf8(out_str).expect("parser output failed"),
))
} else {
Err(reedline_repl_rs::Error::UnknownCommand(
"Command execute failed".to_string(),
))
}
}
18 changes: 17 additions & 1 deletion project/repl/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
mod hello;
mod ls;
mod exit;

pub use self::exit::exit;
pub use self::hello::hello;
pub use self::ls::ls;

use clap::Parser;
use clap::{Args, Parser};

type ReplCommandResult = Result<Option<String>, reedline_repl_rs::Error>;

#[derive(Debug, Parser)]
#[command(name = "Repl", about = "Repl tools")]
pub enum ReplCommand {
Hello { who: String },
LS(LsOptions),
Exit
}

#[derive(Args, Debug)]
pub struct LsOptions {
#[arg(long, default_value = ".")]
pub dir: String,
#[arg(short, default_value = "false")]
pub list_detail: bool,
#[arg(short, default_value = "false")]
pub all_files: bool
}
19 changes: 18 additions & 1 deletion project/repl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
mod cli;

use std::collections::VecDeque;

pub use cli::exit;
pub use cli::ReplCommand;
use reedline_repl_rs::CallBackMap;

pub type ReplCallbacks = CallBackMap<(), reedline_repl_rs::Error>;
pub struct ReplContext {
list: VecDeque<String>,
}
impl ReplContext {
pub fn new() -> Self {
ReplContext {
list: VecDeque::new(),
}
}
}

pub type ReplCallbacks = CallBackMap<ReplContext, reedline_repl_rs::Error>;

pub fn get_callbacks() -> ReplCallbacks {
let mut callbacks = CallBackMap::new();

// add your callback functions here
callbacks.insert("hello".to_string(), cli::hello);
callbacks.insert("ls".to_string(), cli::ls);
callbacks.insert("exit".to_string(), cli::exit);

callbacks
}
8 changes: 5 additions & 3 deletions project/repl/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use reedline_repl_rs::{ Repl, Result};
use repl::{get_callbacks, ReplCommand};
use clap::Command;
use reedline_repl_rs::{Repl, Result};
use repl::{get_callbacks, ReplCommand, ReplContext};

fn main() -> Result<()> {
let callbacks = get_callbacks();
let ctx = ReplContext::new();

let mut repl: Repl<(), reedline_repl_rs::Error> = Repl::new(())
let mut repl = Repl::new(ctx)
.with_name("MyRepl")
.with_description("my repl demo tool")
.with_derived::<ReplCommand>(callbacks);
Expand Down

0 comments on commit b643b47

Please sign in to comment.