https://github.com/hkalbasi/clap-repl
Build nice REPLs using clap and reedline with zero effort
https://github.com/hkalbasi/clap-repl
clap-rs command-line repl rust
Last synced: 10 months ago
JSON representation
Build nice REPLs using clap and reedline with zero effort
- Host: GitHub
- URL: https://github.com/hkalbasi/clap-repl
- Owner: HKalbasi
- License: apache-2.0
- Created: 2023-06-22T08:07:42.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2025-03-14T13:37:04.000Z (over 1 year ago)
- Last Synced: 2025-03-29T11:06:39.520Z (over 1 year ago)
- Topics: clap-rs, command-line, repl, rust
- Language: Rust
- Homepage: https://crates.io/crates/clap-repl
- Size: 43.9 KB
- Stars: 60
- Watchers: 3
- Forks: 5
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE_APACHE
Awesome Lists containing this project
README
# clap-repl
[](https://github.com/HKalbasi/clap-repl/actions/workflows/rust.yml)
[](https://crates.io/crates/clap-repl)
One of the typical user interfaces for prompting commands is the repl (read eval print loop). One of the best ways of representing commands in a repl
is using space separated arguments, which is what terminal shells do. And the way to parse such commands in Rust is the `clap` crate. This crate uses
`clap` and `reedline` to provide such user interface in a way that you only focus on your app logic.
## Features
Thanks to `clap` and `reedline` this crate handles:
* Parsing the space separated commands into your data structure.
* Help flag for each command.
* Verifying the command is valid, generating useful errors and suggestions otherwise.
* Auto complete and hint for the commands.
## Example
```Rust
use std::path::PathBuf;
use clap::{Parser, ValueEnum};
use clap_repl::reedline::{
DefaultPrompt, DefaultPromptSegment, FileBackedHistory, Reedline, Signal,
};
use clap_repl::ClapEditor;
#[derive(Debug, Parser)]
#[command(name = "")] // This name will show up in clap's error messages, so it is important to set it to "".
enum SampleCommand {
Download {
path: PathBuf,
/// Check the integrity of the downloaded object
///
/// Uses SHA256
#[arg(long)]
check_sha: bool,
},
/// A command to upload things.
Upload,
/// Login into the system.
Login {
/// Optional. You will be prompted if you don't provide it.
#[arg(short, long)]
username: Option,
#[arg(short, long, value_enum, default_value_t = Mode::Secure)]
mode: Mode,
},
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Mode {
/// Encrypt the password
Secure,
/// Send the password plain
///
/// This paragraph is ignored because there is no long help text for possible values in clap.
Insecure,
}
fn main() {
let prompt = DefaultPrompt {
left_prompt: DefaultPromptSegment::Basic("simple-example".to_owned()),
..DefaultPrompt::default()
};
let rl = ClapEditor::::builder()
.with_prompt(Box::new(prompt))
.with_editor_hook(|reed| {
// Do custom things with `Reedline` instance here
reed.with_history(Box::new(
FileBackedHistory::with_file(10000, "/tmp/clap-repl-simple-example-history".into())
.unwrap(),
))
})
.build();
rl.repl(|command| {
match command {
SampleCommand::Download { path, check_sha } => {
println!("Downloaded {path:?} with checking = {check_sha}");
}
SampleCommand::Upload => {
println!("Uploaded");
}
SampleCommand::Login { username, mode } => {
// You can use another `reedline::Reedline` inside the loop.
let mut rl = Reedline::create();
let username = username
.unwrap_or_else(|| read_line_with_reedline(&mut rl, "What is your username? "));
let password = read_line_with_reedline(&mut rl, "What is your password? ");
println!("Logged in with {username} and {password} in mode {mode:?}");
}
}
});
}
fn read_line_with_reedline(rl: &mut Reedline, prompt: &str) -> String {
let Signal::Success(x) = rl
.read_line(&DefaultPrompt::new(
DefaultPromptSegment::Basic(prompt.to_owned()),
DefaultPromptSegment::Empty,
))
.unwrap()
else {
panic!();
};
x
}
```


