### Complete rustyrepl REPL example with clap and tokio Source: https://docs.rs/rustyrepl/latest/rustyrepl Defines a CLI with a subcommand, implements ReplCommandProcessor to handle commands and quit/exit, and starts the REPL with a custom prompt. Requires anyhow, clap, rustyrepl, and tokio. ```rust use anyhow::Result; use clap::{Parser, Subcommand}; use rustyrepl::{Repl, ReplCommandProcessor}; /// The enum of sub-commands supported by the CLI #[derive(Subcommand, Clone, Debug)] pub enum Command { /// Execute a test command Test, } /// The general CLI, essentially a wrapper for the sub-commands [Command] #[derive(Parser, Clone, Debug)] pub struct Cli { #[clap(subcommand)] command: Command, } #[derive(Debug)] pub struct CliProcessor {} #[async_trait::async_trait] impl ReplCommandProcessor for CliProcessor { fn is_quit(&self, command: &str) -> bool { matches!(command, "quit" | "exit") } async fn process_command(&self, command: Cli) -> Result<()> { match command.command { Command::Test => println!("A wild test appeared!"), } Ok(()) } } // MAIN // #[tokio::main] async fn main() -> Result<()> { let processor: Box> = Box::new(CliProcessor {}); let mut repl = Repl::::new(processor, None, Some(">>".to_string()))?; repl.process().await } ``` -------------------------------- ### Example REPL interaction showing help output Source: https://docs.rs/rustyrepl/latest/rustyrepl Shows the output of running the example REPL and typing 'help', displaying the generated help menu with usage and available commands. ```text >> help The general CLI, essentially a wrapper for the sub-commands [Commands] Usage: repl-interface Commands: test Execute a test command help Print this message or the help of the given subcommand(s) Options: -h, --help Print help ``` -------------------------------- ### Example is_quit implementation Source: https://docs.rs/rustyrepl/latest/rustyrepl/trait.ReplCommandProcessor.html Example implementation of is_quit that checks for English quit commands 'quit' or 'exit'. The command is expected to be a trimmed lowercase string. ```rust fn is_quit(command: &str) -> bool { matches!(command, "quit" | "exit") } ``` -------------------------------- ### Repl::new Source: https://docs.rs/rustyrepl/latest/rustyrepl/struct.Repl.html Constructs a new Repl instance with an optional history file and prompt. Returns a Result containing the Repl instance or an error. ```APIDOC ## [Constructor] Repl::new ### Description Constructs a new REPL interface. The optional history file is used for command history navigation, and the prompt defaults to ">>" if not provided. ### Method Constructor ### Signature `pub fn new(command_processor: Box>, history_file: Option, prompt: Option) -> Result` ### Parameters - **command_processor** (Box>) - Required - The command processor that handles commands. - **history_file** (Option) - Required - The optional command history file. Can be a full path, relative path, directory, or just the end filename. If None, no history is loaded or stored. - **prompt** (Option) - Required - The prompt to display to the user. Defaults to ">>" if None. ### Return Value `Result` - A Result containing the newly constructed Repl instance or an error. ``` -------------------------------- ### Repl::process Source: https://docs.rs/rustyrepl/latest/rustyrepl/struct.Repl.html Executes the REPL loop, prompting for user input and processing the results. Returns a Result indicating success or failure. ```APIDOC ## [Method] Repl::process ### Description Executes the REPL, prompting for user input and processing the results. ### Method Method ### Signature `pub fn process(&mut self) -> Result<()>` ### Parameters None ### Return Value `Result<()>` - A Result indicating whether the processing loop executed successfully. ``` -------------------------------- ### rustyrepl Repl struct and process_block! macro Source: https://docs.rs/rustyrepl/latest/src/rustyrepl/repl/mod.rs.html The main module of the rustyrepl crate, defining the Repl struct and the process_block! macro that implements the REPL loop. It handles reading lines, parsing commands with clap, and managing history, with async support toggled by the 'async' feature. ```rust 1// Copyright (c) Sean Lawlor 2// 3// This source code is licensed under the MIT license found in the 4// LICENSE file in the root directory of this source tree. 5 6use anyhow::Result; 7use log::{debug, error, info, warn}; 8use rustyline::error::ReadlineError; 9use rustyline::Editor; 10use std::{ 11 marker::PhantomData, 12 path::{Path, PathBuf}, 13}; 14 15use crate::commands::ReplCommandProcessor; 16 17const DEFAULT_HISTORY_FILE_NAME: &str = ".repl_history"; 18 19#[cfg(test)] mod tests; 20 21#[cfg(not(feature = "async"))] 22macro_rules! get_specific_processing_call { 23 ($self:ident, $cli:expr) => { 24 $self.command_processor.process_command($cli)?; 25 }; 26} 27 28#[cfg(feature = "async")] 29macro_rules! get_specific_processing_call { 30 ($self:ident, $cli:expr) => { 31 $self.command_processor.process_command($cli).await? 32 }; } 33 34/// The [process_block!] macro toggles between the async and sync 35/// definitions of the logic in the process() function. Depending on the 36/// feature enabled, we will select the correct underlying implementation 37/// as necessary 38macro_rules! process_block { 39 ( $self:ident ) => { 40 { 41 loop { 42 let readline = $self.editor.readline(&$self.prompt); 43 match readline { 44 Ok(line) => { 45 let parts: Vec<&str> = line.split(' ').collect(); 46 let mut command = String::new(); 47 if let Some(head) = parts.first() { 48 command = String::from(*head); 49 } 50 match command.to_lowercase().as_ref() { 51 "" => {} // Loop, someone hit enter needlessly 52 maybe_quit if $self.command_processor.is_quit(maybe_quit) => break, // check for quit/exit 53 _ => { 54 // We're only appending valid commands to the history trail 55 $self.editor.add_history_entry(line.as_str()); 56 57 let mut cmd_parts: Vec<&str> = vec!["repl-interface"]; 58 cmd_parts.extend(line.split(' ').collect::>().iter().copied()); 59 match C::try_parse_from(cmd_parts.into_iter()) { 60 Ok(cli) => { 61 // Call the underlying processing logic 62 get_specific_processing_call!($self, cli); 63 } 64 Err(clap_err) => match clap::Error::kind(&clap_err) { 65 66 clap::error::ErrorKind::DisplayHelp | clap::error::ErrorKind::DisplayVersion => { 67 println!("{}", clap_err); } 68 _ => { 69 warn!( "Invalid command (type 'help' for the help menu\r\n{}", clap_err ); } }, } } } } Err(ReadlineError::Interrupted) => break, // CTRL-C Err(ReadlineError::Eof) => break, // CTRL-D Err(err) => { error!("Error: {:?}", err); break; } } } $self.close_history(); Ok(()) } }; } /// Represents the REPL interface and processing loop #[derive(Debug)] pub struct Repl where C: clap::Parser, { /// The REPL editor interface for the command prompt editor: Editor<()>, /// The history file history: Option, /// User-provided command processor responsible for processing parsed command instructions and /// executing on them command_processor: Box>, /// The prompt to the interface (defaults to ">>") prompt: String, /// Phantom holder for the command structure enum _command_type: PhantomData, } impl Repl where C: clap::Parser, { // =================== Private Functions =================== // /// Format the history file name to a full path for rustyline ``` -------------------------------- ### trait ReplCommandProcessor Source: https://docs.rs/rustyrepl/latest/rustyrepl/trait.ReplCommandProcessor.html Defines the required methods for a command processor: process_command and is_quit. Implementors must handle command processing and quit detection. ```APIDOC ## Trait ReplCommandProcessor ### Description Defines the required methods for a command processor: process_command and is_quit. Implementors must handle command processing and quit detection. ### Definition ```rust pub trait ReplCommandProcessor: Debug where C: Parser, { // Required methods fn process_command(&self, command: C) -> Result<()>; fn is_quit(&self, command: &str) -> bool; } ``` ### Required Methods #### fn process_command(&self, command: C) -> Result<()> Process the supplied command which is a clap::Parser structure #### fn is_quit(&self, command: &str) -> bool Determine if the supplied command is a "quit" operation. This will be formatted into a trimmed string of lowercase letters. Example matching might check (in English) ```rust fn is_quit(command: &str) -> bool { matches!(command, "quit" | "exit") } ``` ### Dyn Compatibility This trait **is** dyn compatible. _In older versions of Rust, dyn compatibility was called "object safety"_. ### Implementors - (List of implementors not provided in source) ``` -------------------------------- ### Add rustyrepl dependency to Cargo.toml Source: https://docs.rs/rustyrepl/latest/rustyrepl Adds the rustyrepl crate version 0.2 to the project's dependencies. ```toml [dependencies] rustyrepl = "0.2" ``` -------------------------------- ### Async ReplCommandProcessor trait definition (feature = "async") Source: https://docs.rs/rustyrepl/latest/src/rustyrepl/commands.rs.html Defines the async version of the ReplCommandProcessor trait, used when the 'async' feature is enabled. It requires the command type to implement clap::Parser and provides an async process_command method and an is_quit method. The is_quit method receives a trimmed lowercase string and can match against commands like "quit" or "exit". ```rust // Copyright (c) Sean Lawlor // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. //! Represents a processor of REPL commands from a user's Clap parsed input use anyhow::Result; #[cfg(feature = "async")] #[cfg_attr(feature = "async", async_trait::async_trait)] pub trait ReplCommandProcessor: std::fmt::Debug where C: clap::Parser, { /// Process the supplied command which is a clap::Parser structure async fn process_command(&self, command: C) -> Result<()>; /// Determine if the supplied command is a "quit" operation. This will be /// formatted into a trimmed string of lowercase letters. Example matching might /// check (in English) /// /// ``` /// fn is_quit(command: &str) -> bool { /// matches!(command, "quit" | "exit") /// } /// ``` fn is_quit(&self, command: &str) -> bool; } ``` -------------------------------- ### Sync ReplCommandProcessor trait definition (without async feature) Source: https://docs.rs/rustyrepl/latest/src/rustyrepl/commands.rs.html Defines the synchronous version of the ReplCommandProcessor trait, used when the 'async' feature is disabled. It provides a synchronous process_command method and an is_quit method. The is_quit method receives a trimmed lowercase string and can match against commands like "quit" or "exit". ```rust #[cfg(not(feature = "async"))] pub trait ReplCommandProcessor: std::fmt::Debug where C: clap::Parser, { /// Process the supplied command which is a clap::Parser structure fn process_command(&self, command: C) -> Result<()>; /// Determine if the supplied command is a "quit" operation. This will be /// formatted into a trimmed string of lowercase letters. Example matching might /// check (in English) /// /// ``` /// fn is_quit(command: &str) -> bool { /// matches!(command, "quit" | "exit") /// } /// ``` fn is_quit(&self, command: &str) -> bool; } ``` -------------------------------- ### Trait definition for ReplCommandProcessor Source: https://docs.rs/rustyrepl/latest/rustyrepl/trait.ReplCommandProcessor.html Defines the ReplCommandProcessor trait with a generic parameter C constrained by clap::Parser. It requires implementing process_command and is_quit methods. The trait is dyn compatible. ```rust pub trait ReplCommandProcessor: Debug where C: Parser, { // Required methods fn process_command(&self, command: C) -> Result<()>; fn is_quit(&self, command: &str) -> bool; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.