From d413b74d45493919091168b6d1e6074553f3ea2f Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Fri, 28 Jun 2024 18:10:08 +0200 Subject: [PATCH] feat(cli): add repl example without using library items --- members/libpt-cli/examples/repl.rs | 133 +++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 members/libpt-cli/examples/repl.rs diff --git a/members/libpt-cli/examples/repl.rs b/members/libpt-cli/examples/repl.rs new file mode 100644 index 0000000..106813e --- /dev/null +++ b/members/libpt-cli/examples/repl.rs @@ -0,0 +1,133 @@ +use libpt_cli::repl::REPL_HELP_TEMPLATE; +use libpt_cli::{clap, dialoguer, printing}; +use libpt_log::{debug, trace, Level, Logger}; + +use clap::{Parser, Subcommand}; + +/// This is the help menu of the repl +/// +/// More text here +#[derive(Parser, Debug)] +#[command(multicall = true)] +pub struct Repl { + /// the command you want to execute, along with its args + #[command(subcommand)] + command: ReplCommand, +} + +#[derive(Subcommand, Debug)] +enum ReplCommand { + /// wait for LEN seconds + Wait { + /// wait so long + len: u64, + }, + /// echo the given texts + Echo { + /// the text you want to print + text: Vec, + /// print with a fancy border and colors + #[arg(short, long)] + fancy: bool, + }, + /// hello world + Hello, + /// leave the repl + Exit, +} + +// TODO: somehow autogenerate this!!! +pub struct MyCompletion { + options: Vec, +} +impl Default for MyCompletion { + fn default() -> Self { + MyCompletion { + options: vec![ + "help".to_string(), + "?".to_string(), + "list".to_string(), + "publish".to_string(), + "unpublish".to_string(), + "delete".to_string(), + "read".to_string(), + "show".to_string(), + "new".to_string(), + "ls".to_string(), + ], + } + } +} + +impl dialoguer::Completion for MyCompletion { + /// Simple completion implementation based on substring + fn get(&self, input: &str) -> Option { + let matches = self + .options + .iter() + .filter(|option| option.starts_with(input)) + .collect::>(); + + if matches.len() == 1 { + Some(matches[0].to_string()) + } else { + None + } + } +} + +fn main() -> anyhow::Result<()> { + let _logger = Logger::builder() + .show_time(false) + .max_level(Level::DEBUG) + .build(); + + let mut buf: String = String::new(); + let mut buf_preparsed: Vec; + let completion = MyCompletion::default(); + let mut history = dialoguer::BasicHistory::new(); + + debug!("entering the repl"); + loop { + buf.clear(); + + buf = dialoguer::Input::with_theme(&dialoguer::theme::ColorfulTheme::default()) + .completion_with(&completion) + .history_with(&mut history) + .interact_text()?; + + buf_preparsed = Vec::new(); + buf_preparsed.extend(shlex::split(&buf).unwrap_or_default()); + + trace!("read input: {buf_preparsed:?}"); + + let options = match Repl::try_parse_from(buf_preparsed) { + Ok(c) => c, + Err(e) => { + println!("{e}"); + continue; + } + }; + + match options.command { + ReplCommand::Exit => break, + ReplCommand::Wait { len } => { + debug!("len: {len}"); + let spinner = indicatif::ProgressBar::new_spinner(); + spinner.enable_steady_tick(std::time::Duration::from_millis(100)); + std::thread::sleep(std::time::Duration::from_secs(len)); + spinner.finish(); + } + ReplCommand::Hello => println!("Hello!"), + ReplCommand::Echo { text, fancy } => { + if !fancy { + println!("{}", text.concat()) + } + else { + printing::blockprint(text.concat(), console::Color::Cyan) + } + } + } + } + Ok(()) +}