diff options
Diffstat (limited to 'mingling/src')
| -rw-r--r-- | mingling/src/example_docs.rs | 68 | ||||
| -rw-r--r-- | mingling/src/setups.rs | 6 | ||||
| -rw-r--r-- | mingling/src/setups/repl_basic.rs | 86 |
3 files changed, 133 insertions, 27 deletions
diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs index ab725de..8c93f4b 100644 --- a/mingling/src/example_docs.rs +++ b/mingling/src/example_docs.rs @@ -584,7 +584,12 @@ pub mod example_picker {} /// /// main.rs /// ```ignore -/// use mingling::{REPL, hook::ProgramHook, prelude::*, this}; +/// use mingling::{ +/// hook::ProgramHook, +/// prelude::*, +/// setup::{BasicREPLOutputSetup, BasicREPLPromptSetup, BasicREPLReadlineSetup}, +/// this, REPL, +/// }; /// use std::{env::current_dir, path::PathBuf}; /// /// // Resource to store the current directory @@ -611,34 +616,29 @@ pub mod example_picker {} /// program.with_dispatcher(ChangeDirectoryCommand); /// program.with_dispatcher(ListCommand); /// program.with_dispatcher(ExitCommand); +/// program.with_dispatcher(ClearCommand); +/// +/// // Add setups +/// program.with_setup(BasicREPLReadlineSetup); +/// program.with_setup(BasicREPLOutputSetup); +/// program.with_setup(BasicREPLPromptSetup::func(|| { +/// let res = this::<ThisProgram>().res::<CurrentDir>().unwrap(); +/// let dir_str: String = res.dir.to_string_lossy().into(); +/// let prompt = format!( +/// "{}> ", +/// dir_str +/// .replace(&['/', '\\'][..], ">") +/// .trim_start_matches('>') +/// .trim_end_matches('>') +/// ); +/// prompt +/// })); /// /// // Add hooks to handle REPL-related events -/// program.with_hook( -/// ProgramHook::empty() -/// .on_repl_begin(|| { -/// // Print welcome message -/// println!("Welcome!") -/// }) -/// .on_repl_pre_readline(|| { -/// // Print prompt -/// let res = this::<ThisProgram>().res::<CurrentDir>().unwrap(); -/// let dir_str: String = res.dir.to_string_lossy().into(); -/// let prompt = format!( -/// "{}> ", -/// dir_str -/// .replace(&['/', '\\'][..], ">") -/// .trim_start_matches('>') -/// .trim_end_matches('>') -/// ); -/// print!("{}", prompt) -/// }) -/// .on_repl_receive_result(|r| { -/// // Print output -/// if !r.is_empty() { -/// println!("{}", r.trim()) -/// } -/// }), -/// ); +/// program.with_hook(ProgramHook::empty().on_repl_begin(|| { +/// // Print welcome message +/// println!("Welcome!") +/// })); /// /// // Start the REPL loop /// program.exec_repl(); @@ -651,6 +651,7 @@ pub mod example_picker {} /// dispatcher!("cd", ChangeDirectoryCommand => ChangeDirectoryEntry); /// dispatcher!("ls", ListCommand => ListEntry); /// dispatcher!("exit", ExitCommand => ExitEntry); +/// dispatcher!("clear", ClearCommand => ClearEntry); /// /// // Define data needed for the cd command's execution phase /// pack!(StateChangeDirectory = String); @@ -719,12 +720,25 @@ pub mod example_picker {} /// repl.exit = true; /// } /// +/// // Handle clear command event +/// #[chain] +/// fn handle_clear(_prev: ClearEntry) { +/// // Clear the terminal screen +/// print!("\x1B[2J\x1B[1;1H"); +/// } +/// /// // Handle path not found event /// #[renderer] /// fn render_error_directory_not_exist(err: ErrorDirectoryNotExist) { /// r_println!("Directory not found: {}", err.inner.display()) /// } /// +/// // Handle dispatcher not found event +/// #[renderer] +/// fn dispatcher_not_found(prev: DispatcherNotFound) { +/// r_println!("Command not found: \"{}\"", prev.join(", ")) +/// } +/// /// gen_program!(); /// ``` pub mod example_repl {} diff --git a/mingling/src/setups.rs b/mingling/src/setups.rs index 351112b..b4fad58 100644 --- a/mingling/src/setups.rs +++ b/mingling/src/setups.rs @@ -9,3 +9,9 @@ mod general_renderer; #[cfg(feature = "general_renderer")] pub use general_renderer::*; + +#[cfg(feature = "repl")] +mod repl_basic; + +#[cfg(feature = "repl")] +pub use repl_basic::*; diff --git a/mingling/src/setups/repl_basic.rs b/mingling/src/setups/repl_basic.rs new file mode 100644 index 0000000..5884489 --- /dev/null +++ b/mingling/src/setups/repl_basic.rs @@ -0,0 +1,86 @@ +use std::io::Write; + +use mingling_core::{Program, ProgramCollect, hook::ProgramHook, setup::ProgramSetup}; + +/// Provides basic Readline capability for the REPL. +pub struct BasicREPLReadlineSetup; +impl<C> ProgramSetup<C> for BasicREPLReadlineSetup +where + C: ProgramCollect<Enum = C>, +{ + fn setup(&mut self, program: &mut Program<C>) { + program.with_hook(ProgramHook::empty().on_repl_readline(|| readline().ok())); + } +} + +/// A basic REPL prompt that displays a prompt string and reads input from the user. +/// +/// **Note:** This setup uses static [`OnceLock`](std::sync::OnceLock) internally, +/// meaning only the last configured instance will take effect globally. +/// Do not configure multiple prompts with different values — only one will be used. +pub enum BasicREPLPromptSetup { + Prompt(String), + Func(fn() -> String), +} + +impl BasicREPLPromptSetup { + /// Creates a new [`BasicREPLPrompt`] with the given prompt string. + pub fn simple(prompt: impl Into<String>) -> Self { + Self::Prompt(prompt.into()) + } + + /// Creates a new [`BasicREPLPrompt`] with the given function. + pub fn func(func: fn() -> String) -> Self { + Self::Func(func) + } +} + +impl<C> ProgramSetup<C> for BasicREPLPromptSetup +where + C: ProgramCollect<Enum = C>, +{ + fn setup(&mut self, program: &mut Program<C>) { + match self { + BasicREPLPromptSetup::Prompt(prompt) => { + static PROMPT: std::sync::OnceLock<String> = std::sync::OnceLock::new(); + let _ = PROMPT.set(prompt.clone()); + fn print_prompt() { + print!("{}", PROMPT.get().unwrap()); + let _ = std::io::stdout().flush(); + } + program.with_hook(ProgramHook::empty().on_repl_pre_readline(print_prompt)); + } + BasicREPLPromptSetup::Func(f) => { + static FUNC: std::sync::OnceLock<fn() -> String> = std::sync::OnceLock::new(); + let _ = FUNC.set(*f); + fn print_func_prompt() { + print!("{}", FUNC.get().unwrap()()); + let _ = std::io::stdout().flush(); + } + program.with_hook(ProgramHook::empty().on_repl_pre_readline(print_func_prompt)); + } + } + } +} + +/// Prints the result of each REPL command to stdout. +pub struct BasicREPLOutputSetup; +impl<C> ProgramSetup<C> for BasicREPLOutputSetup +where + C: ProgramCollect<Enum = C>, +{ + fn setup(&mut self, program: &mut Program<C>) { + program.with_hook(ProgramHook::empty().on_repl_receive_result(|r| { + if !r.is_empty() { + println!("{}", r.trim()) + } + })); + } +} + +fn readline() -> Result<String, std::io::Error> { + let mut input = String::new(); + std::io::stdout().flush()?; + std::io::stdin().read_line(&mut input)?; + Ok(input.trim().to_string()) +} |
