rusty_repl 0.3.0

REPL library with customisable prompts and clean terminal management.
Documentation
//! High-level REPL controller for managing terminal state, prompts, and input flow.
//!
//! [`Repl`] orchestrates an interactive command-line session by tying together
//! terminal management, input handling, and prompt configuration into a single,
//! easy-to-use interface.
//!
//! # Key Features
//! - Safe terminal lifecycle management with alternate screen, hidden cursor, and cleanup.
//! - Clean separation between user logic and terminal/REPL infrastructure.
//! - Extensibility via custom prompts (`Prompt`) and keyword highlighting (`KeywordStyle`).
//!
//! # Example
//! ```no_run
//! use rusty_repl::{Repl, ReplConfig, CleanPrompt, KeywordStyle};
//! use nu_ansi_term::Color;
//!
//! fn main() -> std::io::Result<()> {
//!     // Configure keyword highlighting
//!     let kw_style = KeywordStyle {
//!         keywords: vec!["help".into(), "exit".into()],
//!         foreground: Color::Green,
//!     };
//!
//!     // Create default prompt
//!     let default_prompt = CleanPrompt::default();
//!
//!     // Build configuration using builder-style methods
//!     let config = ReplConfig::default()
//!         .with_title("Demo REPL")
//!         .with_kw_style(kw_style)
//!         .with_prompt(default_prompt);
//!
//!     let repl = Repl::from(config);
//!
//!     // Start the REPL loop
//!     repl.run(|line| {
//!         if line.trim() == "exit" {
//!             return true;
//!         }
//!         println!("You typed: {line}");
//!         false
//!     })
//! }
//! ```
//!
//! # Lifecycle
//! 1. [`Repl::run`] creates a [`TerminalManager`] instance, entering the alternate screen.
//! 2. An [`InputHandler`] is initialized with the configured prompt and optional keyword highlighting.
//! 3. The input loop reads each line and passes it to your closure.
//! 4. Returning `true` from the closure exits the REPL and restores the terminal.
//!
//! # Notes
//! - Avoid calling `std::process::exit` inside your closure; it bypasses cleanup.
//! - Terminal setup/teardown errors are propagated as `std::io::Result`.
//! - Custom prompts are wrapped in `Arc` automatically, so ownership and borrowing are handled safely.

use crate::repl::{config::ReplConfig, input::InputHandler, terminal::TerminalManager};
use std::{io::Result, sync::Arc};

/// Top-level REPL manager that orchestrates terminal setup and user input.
pub struct Repl {
    config: Arc<ReplConfig>,
}

impl Repl {
    /// Creates a new [`Repl`] with the default configuration.
    ///
    /// Uses a default title ("Rusty REPL") and a default [`CleanPrompt`](crate::repl::prompt::CleanPrompt).
    pub fn new() -> Self {
        Self::from(ReplConfig::default())
    }

    /// Creates a new [`Repl`] from a custom configuration.
    pub fn from(config: ReplConfig) -> Self {
        Self {
            config: Arc::new(config),
        }
    }

    /// Starts the REPL session using the provided input handler closure.
    ///
    /// # Arguments
    ///
    /// * `func` - Closure that processes each line. Returning `true` exits the REPL.
    ///
    /// # Errors
    ///
    /// Returns an [`std::io::Error`] if terminal setup or teardown fails.
    pub fn run<F>(&self, func: F) -> Result<()>
    where
        F: Fn(String) -> bool,
    {
        self.with_terminal(|| {
            let mut input_handler = InputHandler::new(self.config.clone());
            let _ = input_handler.run(func);
        })
    }

    /// Internal helper for terminal setup and teardown.
    ///
    /// - Enters the alternate screen, hides the cursor, and sets the title.
    /// - Executes the provided closure.
    /// - Restores the terminal state when done.
    fn with_terminal<F>(&self, func: F) -> Result<()>
    where
        F: FnOnce(),
    {
        let mut repl = TerminalManager::new(self.config.clone())?;

        func();

        repl.restore()
    }
}