An open API service indexing awesome lists of open source software.

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

Awesome Lists containing this project

README

          

# clap-repl

[![Rust](https://github.com/HKalbasi/clap-repl/actions/workflows/rust.yml/badge.svg)](https://github.com/HKalbasi/clap-repl/actions/workflows/rust.yml)
[![crates.io](https://img.shields.io/crates/v/clap-repl.svg)](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
}
```
![Screenshot from 2023-06-22 11-32-58](https://github.com/HKalbasi/clap-repl/assets/45197576/2c1b2ceb-e562-4536-8b42-4025d5a9674a)
![Screenshot from 2023-06-22 11-35-33](https://github.com/HKalbasi/clap-repl/assets/45197576/bec9110e-a399-41e4-8f63-1c8592338625)
![image](https://github.com/HKalbasi/clap-repl/assets/45197576/a5eb04c0-fafc-479e-ba1a-dd5757f585be)