//! Graph inspection REPL use super::*; use std::{fmt, error}; use std::path::PathBuf; use std::io; use rustyline::error::ReadlineError; use rustyline::Editor; mod env; mod command; mod opcodes; /// Default history file name /// /// # Path lookup /// * To make this an absolute path, start it with `/` /// * To make this path relative to the user's home directory, start it with `~/` (Note: If we are unable to find the user's home directory, it is considered a lookup **failure** (*not* a **disable**) and `calculate_history_path()` will return `Err`.) /// * Otherwise, the path is taken relative to the current working directory /// /// # Notes /// This is only used when the `save-history` feature is enabled. const DEFAULT_HISTORY_FILE: &'static str = "~/.dirstat_history"; /// Get the path to the history file. /// /// # Lookup /// * If the `DIRSTAT_HISTORY` envvar is set and not empty, use this file path. /// * If the `DIRSTAT_HISTORY` envvar is set and empty, saving history is considered **disabled**, we return `Ok(None)`. /// * Otherwise, refer to lookup rules for `DEFAULT_HISTORY_FILE`. pub fn calculate_history_path() -> io::Result> { cfg_if! { if #[cfg(feature="save-history")] { todo!() } else { unreachable!("Tried to calculate repl history path when binary was compiled with history saving perma-disabled.") } } } /// Inspect the graph with commands /// /// # Note /// This function synchronously blocks the current thread. pub fn inspect(cfg: &Config, graph: &HierarchicalINodeGraph) -> Result<(), ReplExitError> { let mut repl = Editor::<()>::new(); //TODO: Change `()` to our completer, when we have a decent idea of how they'll work. cfg_if! { if #[cfg(feature="save-history")] { let history_path = match calculate_history_path() { Ok(Some(path)) => { if let Err(err) = repl.load_history(&path) { cfg_eprintln!(cfg, "Failed to load repl history from {:?}: {}", path, err); } Some(path) }, Ok(None) => None, Err(err) { cfg_eprintln!(cfg, "Failed to find repl history: {}", err); None } } } } let res: Result<(), ReplExitError> = try { loop { let line = repl.readline("> ")?; repl.add_history_entry(&line); //TODO: How to interpret commands? todo!("Interpret commands from `line`."); } }; cfg_if! { if #[cfg(feature="save-history")] { if let Some(path) = history_path { if let Err(err) = repl.save_history(&path) { cfg_eprintln!(cfg, "Failed to save repl history to {:?}: {}", path, err); } } } } res } /// When the inspection repl exists abnormally. #[derive(Debug)] pub enum ReplExitError { ReadLine(ReadlineError), } impl From for ReplExitError { #[inline] fn from(from: ReadlineError) -> Self { Self::ReadLine(from) } } impl error::Error for ReplExitError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(match &self { Self::ReadLine(rl) => rl }) } } impl fmt::Display for ReplExitError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::ReadLine(ReadlineError::Eof) | Self::ReadLine(ReadlineError::Interrupted) => write!(f, "exit"), Self::ReadLine(_) => write!(f, "readline error"), } } }