diff options
Diffstat (limited to 'src/command/client/history.rs')
| -rw-r--r-- | src/command/client/history.rs | 298 |
1 files changed, 0 insertions, 298 deletions
diff --git a/src/command/client/history.rs b/src/command/client/history.rs deleted file mode 100644 index 76c796ef..00000000 --- a/src/command/client/history.rs +++ /dev/null @@ -1,298 +0,0 @@ -use std::{ - env, - fmt::{self, Display}, - io::{StdoutLock, Write}, - time::Duration, -}; - -use atuin_common::utils; -use clap::Subcommand; -use eyre::Result; -use runtime_format::{FormatKey, FormatKeyError, ParsedFmt}; - -use atuin_client::{ - database::{current_context, Database}, - history::History, - settings::Settings, -}; - -#[cfg(feature = "sync")] -use atuin_client::sync; -use log::debug; - -use super::search::format_duration; -use super::search::format_duration_into; - -#[derive(Subcommand)] -#[command(infer_subcommands = true)] -pub enum Cmd { - /// Begins a new command in the history - Start { command: Vec<String> }, - - /// Finishes a new command in the history (adds time, exit code) - End { - id: String, - #[arg(long, short)] - exit: i64, - }, - - /// List all items in history - List { - #[arg(long, short)] - cwd: bool, - - #[arg(long, short)] - session: bool, - - #[arg(long)] - human: bool, - - /// Show only the text of the command - #[arg(long)] - cmd_only: bool, - - /// Available variables: {command}, {directory}, {duration}, {user}, {host} and {time}. - /// Example: --format "{time} - [{duration}] - {directory}$\t{command}" - #[arg(long, short)] - format: Option<String>, - }, - - /// Get the last command ran - Last { - #[arg(long)] - human: bool, - - /// Show only the text of the command - #[arg(long)] - cmd_only: bool, - - /// Available variables: {command}, {directory}, {duration}, {user}, {host} and {time}. - /// Example: --format "{time} - [{duration}] - {directory}$\t{command}" - #[arg(long, short)] - format: Option<String>, - }, -} - -#[derive(Clone, Copy, Debug)] -pub enum ListMode { - Human, - CmdOnly, - Regular, -} - -impl ListMode { - pub const fn from_flags(human: bool, cmd_only: bool) -> Self { - if human { - ListMode::Human - } else if cmd_only { - ListMode::CmdOnly - } else { - ListMode::Regular - } - } -} - -#[allow(clippy::cast_sign_loss)] -pub fn print_list(h: &[History], list_mode: ListMode, format: Option<&str>) { - let w = std::io::stdout(); - let mut w = w.lock(); - - match list_mode { - ListMode::Human => print_human_list(&mut w, h, format), - ListMode::CmdOnly => print_cmd_only(&mut w, h), - ListMode::Regular => print_regular(&mut w, h, format), - } - - w.flush().expect("failed to flush history"); -} - -/// type wrapper around `History` so we can implement traits -struct FmtHistory<'a>(&'a History); - -/// defines how to format the history -impl FormatKey for FmtHistory<'_> { - #[allow(clippy::cast_sign_loss)] - fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> { - match key { - "command" => f.write_str(self.0.command.trim())?, - "directory" => f.write_str(self.0.cwd.trim())?, - "exit" => f.write_str(&self.0.exit.to_string())?, - "duration" => { - let dur = Duration::from_nanos(std::cmp::max(self.0.duration, 0) as u64); - format_duration_into(dur, f)?; - } - "time" => self.0.timestamp.format("%Y-%m-%d %H:%M:%S").fmt(f)?, - "relativetime" => { - let since = chrono::Utc::now() - self.0.timestamp; - let time = format_duration(since.to_std().unwrap_or_default()); - f.write_str(&time)?; - } - "host" => f.write_str( - self.0 - .hostname - .split_once(':') - .map_or(&self.0.hostname, |(host, _)| host), - )?, - "user" => f.write_str(self.0.hostname.split_once(':').map_or("", |(_, user)| user))?, - _ => return Err(FormatKeyError::UnknownKey), - } - Ok(()) - } -} - -fn print_list_with(w: &mut StdoutLock, h: &[History], format: &str) { - let fmt = match ParsedFmt::new(format) { - Ok(fmt) => fmt, - Err(err) => { - eprintln!("ERROR: History formatting failed with the following error: {err}"); - println!("If your formatting string contains curly braces (eg: {{var}}) you need to escape them this way: {{{{var}}."); - std::process::exit(1) - } - }; - - for h in h.iter().rev() { - writeln!(w, "{}", fmt.with_args(&FmtHistory(h))).expect("failed to write history"); - } -} - -pub fn print_human_list(w: &mut StdoutLock, h: &[History], format: Option<&str>) { - let format = format - .unwrap_or("{time} ยท {duration}\t{command}") - .replace("\\t", "\t"); - print_list_with(w, h, &format); -} - -pub fn print_regular(w: &mut StdoutLock, h: &[History], format: Option<&str>) { - let format = format - .unwrap_or("{time}\t{command}\t{duration}") - .replace("\\t", "\t"); - print_list_with(w, h, &format); -} - -pub fn print_cmd_only(w: &mut StdoutLock, h: &[History]) { - for h in h.iter().rev() { - writeln!(w, "{}", h.command.trim()).expect("failed to write history"); - } -} - -impl Cmd { - pub async fn run(&self, settings: &Settings, db: &mut impl Database) -> Result<()> { - let context = current_context(); - - match self { - Self::Start { command: words } => { - let command = words.join(" "); - - if command.starts_with(' ') || settings.history_filter.is_match(&command) { - return Ok(()); - } - - // It's better for atuin to silently fail here and attempt to - // store whatever is ran, than to throw an error to the terminal - let cwd = utils::get_current_dir(); - - let h = History::new(chrono::Utc::now(), command, cwd, -1, -1, None, None, None); - - // print the ID - // we use this as the key for calling end - println!("{}", h.id); - db.save(&h).await?; - Ok(()) - } - - Self::End { id, exit } => { - if id.trim() == "" { - return Ok(()); - } - - let mut h = db.load(id).await?; - - if h.duration > 0 { - debug!("cannot end history - already has duration"); - - // returning OK as this can occur if someone Ctrl-c a prompt - return Ok(()); - } - - h.exit = *exit; - h.duration = chrono::Utc::now().timestamp_nanos() - h.timestamp.timestamp_nanos(); - - db.update(&h).await?; - - if settings.should_sync()? { - #[cfg(feature = "sync")] - { - debug!("running periodic background sync"); - sync::sync(settings, false, db).await?; - } - #[cfg(not(feature = "sync"))] - debug!("not compiled with sync support"); - } else { - debug!("sync disabled! not syncing"); - } - - Ok(()) - } - - Self::List { - session, - cwd, - human, - cmd_only, - format, - } => { - let session = if *session { - Some(env::var("ATUIN_SESSION")?) - } else { - None - }; - let cwd = if *cwd { - Some(utils::get_current_dir()) - } else { - None - }; - - let history = match (session, cwd) { - (None, None) => db.list(settings.filter_mode, &context, None, false).await?, - (None, Some(cwd)) => { - let query = format!("select * from history where cwd = '{cwd}';"); - db.query_history(&query).await? - } - (Some(session), None) => { - let query = format!("select * from history where session = '{session}';"); - db.query_history(&query).await? - } - (Some(session), Some(cwd)) => { - let query = format!( - "select * from history where cwd = '{cwd}' and session = '{session}';", - ); - db.query_history(&query).await? - } - }; - - print_list( - &history, - ListMode::from_flags(*human, *cmd_only), - format.as_deref(), - ); - - Ok(()) - } - - Self::Last { - human, - cmd_only, - format, - } => { - let last = db.last().await?; - print_list( - &[last], - ListMode::from_flags(*human, *cmd_only), - format.as_deref(), - ); - - Ok(()) - } - } - } -} |
