//! 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 < Option < PathBuf > >
{
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 < ReadlineError > 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" ) ,
}
}
}