generated from PlexSheep/baserepo
implement a cli module #85
|
@ -12,6 +12,9 @@ repository.workspace = true
|
||||||
keywords.workspace = true
|
keywords.workspace = true
|
||||||
categories.workspace = true
|
categories.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["log"]
|
default = ["log"]
|
||||||
log = ["dep:libpt-log", "dep:log"]
|
log = ["dep:libpt-log", "dep:log"]
|
||||||
|
@ -22,6 +25,7 @@ clap = { version = "4.5.7", features = ["derive"] }
|
||||||
comfy-table = "7.1.1"
|
comfy-table = "7.1.1"
|
||||||
console = "0.15.8"
|
console = "0.15.8"
|
||||||
dialoguer = { version = "0.11.0", features = ["completion", "history"] }
|
dialoguer = { version = "0.11.0", features = ["completion", "history"] }
|
||||||
|
embed-doc-image = "0.1.4"
|
||||||
exitcode = "1.1.2"
|
exitcode = "1.1.2"
|
||||||
human-panic = "2.0.0"
|
human-panic = "2.0.0"
|
||||||
indicatif = "0.17.8"
|
indicatif = "0.17.8"
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 142 KiB |
|
@ -1,13 +1,61 @@
|
||||||
|
//! This module implements a default repl that fullfills the [Repl] trait
|
||||||
|
//!
|
||||||
|
//! You can implement your own [Repl] if you want.
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use super::Repl;
|
use super::Repl;
|
||||||
|
|
||||||
|
use embed_doc_image::embed_doc_image;
|
||||||
|
|
||||||
|
/// [clap] help template with only usage and commands/options
|
||||||
|
pub const REPL_HELP_TEMPLATE: &str = r#"{usage-heading} {usage}
|
||||||
|
|
||||||
|
{all-args}{tab}
|
||||||
|
"#;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use dialoguer::{BasicHistory, Completion};
|
use dialoguer::{BasicHistory, Completion};
|
||||||
use libpt_log::trace;
|
use libpt_log::trace;
|
||||||
|
|
||||||
|
#[allow(clippy::needless_doctest_main)] // It makes the example look better
|
||||||
|
/// Default implementation for a REPL
|
||||||
|
///
|
||||||
|
/// Note that you need to define the commands by yourself with a Subcommands enum.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use libpt_cli::repl::{DefaultRepl, Repl};
|
||||||
|
/// use libpt_cli::clap::Subcommand;
|
||||||
|
/// use libpt_cli::strum::EnumIter;
|
||||||
|
///
|
||||||
|
/// #[derive(Subcommand, Debug, EnumIter, Clone)]
|
||||||
|
/// enum ReplCommand {
|
||||||
|
/// /// hello world
|
||||||
|
/// Hello,
|
||||||
|
/// /// leave the repl
|
||||||
|
/// Exit,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let mut repl = DefaultRepl::<ReplCommand>::default();
|
||||||
|
/// loop {
|
||||||
|
/// repl.step().unwrap();
|
||||||
|
/// match repl.command().to_owned().unwrap() {
|
||||||
|
/// ReplCommand::Hello => println!("Hello"),
|
||||||
|
/// ReplCommand::Exit => break,
|
||||||
|
/// _ => (),
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// **Screenshot**
|
||||||
|
///
|
||||||
|
/// ![Screenshot of an example program with a REPL][repl_screenshot]
|
||||||
|
#[embed_doc_image("repl_screenshot", "data/media/repl.png")]
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(multicall = true)]
|
#[command(multicall = true, help_template = REPL_HELP_TEMPLATE)]
|
||||||
pub struct DefaultRepl<C>
|
pub struct DefaultRepl<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug,
|
||||||
|
@ -31,7 +79,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct DefaultReplCompletion<C>
|
struct DefaultReplCompletion<C>
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug,
|
||||||
C: Subcommand,
|
C: Subcommand,
|
||||||
|
@ -58,12 +106,6 @@ where
|
||||||
fn command(&self) -> &Option<C> {
|
fn command(&self) -> &Option<C> {
|
||||||
&self.command
|
&self.command
|
||||||
}
|
}
|
||||||
#[allow(refining_impl_trait)]
|
|
||||||
fn completion() -> DefaultReplCompletion<C> {
|
|
||||||
DefaultReplCompletion {
|
|
||||||
commands: std::marker::PhantomData::<C>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn step(&mut self) -> Result<(), super::error::ReplError> {
|
fn step(&mut self) -> Result<(), super::error::ReplError> {
|
||||||
self.buf.clear();
|
self.buf.clear();
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Errors for the Repl module
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
//! Create easy and well defined REPLs
|
||||||
|
//!
|
||||||
|
//! A REPL is a [Read-Eval-Print-Loop](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop).
|
||||||
|
//! Well known examples for REPLs are shells (like bash).
|
||||||
|
//!
|
||||||
|
//! This module offers a convenient way to create a well-defined REPL without a lot of complicated
|
||||||
|
//! code and with a visually pleasing aesthetic. An example REPL implementation can be found in the
|
||||||
|
//! examples.
|
||||||
|
//!
|
||||||
|
//! The basic idea is that the user defines the commands with an enum and uses [claps](clap)
|
||||||
|
//! `#[derive(Subcommand)]`. A loop is then used to read from the stdin into a buffer, that buffer
|
||||||
|
//! is put to [clap] for parsing, similar to how [clap] would parse commandline arguments.
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -6,8 +19,10 @@ mod default;
|
||||||
pub use default::*;
|
pub use default::*;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use dialoguer::Completion;
|
|
||||||
|
|
||||||
|
/// Common Trait for repl objects
|
||||||
|
///
|
||||||
|
/// Unless you want to implement custom features (not just commands), just use [DefaultRepl].
|
||||||
pub trait Repl<C>: Parser + Debug
|
pub trait Repl<C>: Parser + Debug
|
||||||
where
|
where
|
||||||
C: Debug,
|
C: Debug,
|
||||||
|
@ -18,12 +33,12 @@ where
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
/// get the command that was parsed from user input
|
/// get the command that was parsed from user input
|
||||||
///
|
///
|
||||||
/// Will only be [None] if the repl has not had [step] executed yet.
|
/// Will only be [None] if the repl has not had [step](Repl::step) executed yet.
|
||||||
fn command(&self) -> &Option<C>;
|
fn command(&self) -> &Option<C>;
|
||||||
/// return all possible commands in this repl
|
|
||||||
fn completion() -> impl Completion;
|
|
||||||
/// advance the repl to the next iteration of the main loop
|
/// advance the repl to the next iteration of the main loop
|
||||||
///
|
///
|
||||||
/// This should be used at the start of your loop
|
/// This should be used at the start of your loop.
|
||||||
|
///
|
||||||
|
/// Note that the help menu is an Error: [clap::error::ErrorKind::DisplayHelp]
|
||||||
fn step(&mut self) -> Result<(), ReplError>;
|
fn step(&mut self) -> Result<(), ReplError>;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue