diff options
Diffstat (limited to 'src/command/client')
-rw-r--r-- | src/command/client/history.rs | 98 | ||||
-rw-r--r-- | src/command/client/search.rs | 182 |
2 files changed, 152 insertions, 128 deletions
diff --git a/src/command/client/history.rs b/src/command/client/history.rs index 4f96c444..6e265e30 100644 --- a/src/command/client/history.rs +++ b/src/command/client/history.rs @@ -1,10 +1,9 @@ use std::env; -use std::io::Write; +use std::io::{StdoutLock, Write}; use std::time::Duration; use clap::Subcommand; use eyre::Result; -use tabwriter::TabWriter; use atuin_client::database::{current_context, Database}; use atuin_client::history::History; @@ -53,44 +52,75 @@ pub enum Cmd { }, } -#[allow(clippy::cast_sign_loss)] -pub fn print_list(h: &[History], human: bool, cmd_only: bool) { - let mut writer = TabWriter::new(std::io::stdout()).padding(2); +#[derive(Clone, Copy, Debug)] +pub enum ListMode { + Human, + CmdOnly, + Regular, +} - let lines = h.iter().rev().map(|h| { +impl ListMode { + pub const fn from_flags(human: bool, cmd_only: bool) -> Self { if human { - let duration = humantime::format_duration(Duration::from_nanos(std::cmp::max( - h.duration, 0, - ) as u64)) - .to_string(); - let duration: Vec<&str> = duration.split(' ').collect(); - let duration = duration[0]; - - format!( - "{}\t{}\t{}\n", - h.timestamp.format("%Y-%m-%d %H:%M:%S"), - h.command.trim(), - duration, - ) + ListMode::Human } else if cmd_only { - format!("{}\n", h.command.trim()) + ListMode::CmdOnly } else { - format!( - "{}\t{}\t{}\n", - h.timestamp.timestamp_nanos(), - h.command.trim(), - h.duration - ) + ListMode::Regular } - }); + } +} + +#[allow(clippy::cast_sign_loss)] +pub fn print_list(h: &[History], list_mode: ListMode) { + let w = std::io::stdout(); + let mut w = w.lock(); + + match list_mode { + ListMode::Human => print_human_list(&mut w, h), + ListMode::CmdOnly => print_cmd_only(&mut w, h), + ListMode::Regular => print_regular(&mut w, h), + } + + w.flush().expect("failed to flush history"); +} + +#[allow(clippy::cast_sign_loss)] +pub fn print_human_list(w: &mut StdoutLock, h: &[History]) { + for h in h.iter().rev() { + let duration = + humantime::format_duration(Duration::from_nanos(std::cmp::max(h.duration, 0) as u64)) + .to_string(); + let duration: Vec<&str> = duration.split(' ').collect(); + let duration = duration[0]; + + let time = h.timestamp.format("%Y-%m-%d %H:%M:%S"); + let cmd = h.command.trim(); + + writeln!(w, "{time} ยท {duration}\t{cmd}").expect("failed to write history"); + } +} - for i in lines { - writer - .write_all(i.as_bytes()) - .expect("failed to write to tab writer"); +#[allow(clippy::cast_sign_loss)] +pub fn print_regular(w: &mut StdoutLock, h: &[History]) { + for h in h.iter().rev() { + let duration = + humantime::format_duration(Duration::from_nanos(std::cmp::max(h.duration, 0) as u64)) + .to_string(); + let duration: Vec<&str> = duration.split(' ').collect(); + let duration = duration[0]; + + let time = h.timestamp.format("%Y-%m-%d %H:%M:%S"); + let cmd = h.command.trim(); + + writeln!(w, "{time}\t{cmd}\t{duration}").expect("failed to write history"); } +} - writer.flush().expect("failed to flush tab writer"); +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 { @@ -195,14 +225,14 @@ impl Cmd { } }; - print_list(&history, *human, *cmd_only); + print_list(&history, ListMode::from_flags(*human, *cmd_only)); Ok(()) } Self::Last { human, cmd_only } => { let last = db.last().await?; - print_list(&[last], *human, *cmd_only); + print_list(&[last], ListMode::from_flags(*human, *cmd_only)); Ok(()) } diff --git a/src/command/client/search.rs b/src/command/client/search.rs index a913e161..45b1f978 100644 --- a/src/command/client/search.rs +++ b/src/command/client/search.rs @@ -23,6 +23,7 @@ use atuin_client::{ }; use super::event::{Event, Events}; +use super::history::ListMode; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -73,21 +74,33 @@ impl Cmd { db: &mut (impl Database + Send + Sync), settings: &Settings, ) -> Result<()> { - run( - settings, - self.cwd, - self.exit, - self.interactive, - self.human, - self.exclude_exit, - self.exclude_cwd, - self.before, - self.after, - self.cmd_only, - &self.query, - db, - ) - .await + if self.interactive { + let item = select_history( + &self.query, + settings.search_mode, + settings.filter_mode, + settings.style, + db, + ) + .await?; + eprintln!("{}", item); + } else { + let list_mode = ListMode::from_flags(self.human, self.cmd_only); + run_non_interactive( + settings, + list_mode, + self.cwd, + self.exit, + self.exclude_exit, + self.exclude_cwd, + self.before, + self.after, + &self.query, + db, + ) + .await?; + }; + Ok(()) } } @@ -196,7 +209,7 @@ impl State { let duration = Span::styled( duration, - Style::default().fg(if m.exit == 0 || m.duration == -1 { + Style::default().fg(if m.success() { Color::Green } else { Color::Red @@ -565,117 +578,98 @@ async fn select_history( // This is supposed to more-or-less mirror the command line version, so ofc // it is going to have a lot of args #[allow(clippy::too_many_arguments)] -pub async fn run( +async fn run_non_interactive( settings: &Settings, + list_mode: ListMode, cwd: Option<String>, exit: Option<i64>, - interactive: bool, - human: bool, exclude_exit: Option<i64>, exclude_cwd: Option<String>, before: Option<String>, after: Option<String>, - cmd_only: bool, query: &[String], db: &mut (impl Database + Send + Sync), ) -> Result<()> { - let dir = if let Some(cwd) = cwd { - if cwd == "." { - let current = std::env::current_dir()?; - let current = current.as_os_str(); - let current = current.to_str().unwrap(); + let dir = if cwd.as_deref() == Some(".") { + let current = std::env::current_dir()?; + let current = current.as_os_str(); + let current = current.to_str().unwrap(); - Some(current.to_owned()) - } else { - Some(cwd) - } + Some(current.to_owned()) } else { - None + cwd }; - if interactive { - let item = select_history( - query, + let context = current_context(); + + let results = db + .search( + None, settings.search_mode, settings.filter_mode, - settings.style, - db, + &context, + query.join(" ").as_str(), ) .await?; - eprintln!("{}", item); - } else { - let context = current_context(); - - let results = db - .search( - None, - settings.search_mode, - settings.filter_mode, - &context, - query.join(" ").as_str(), - ) - .await?; - // TODO: This filtering would be better done in the SQL query, I just - // need a nice way of building queries. - let results: Vec<History> = results - .iter() - .filter(|h| { - if let Some(exit) = exit { - if h.exit != exit { - return false; - } + // TODO: This filtering would be better done in the SQL query, I just + // need a nice way of building queries. + let results: Vec<History> = results + .iter() + .filter(|h| { + if let Some(exit) = exit { + if h.exit != exit { + return false; } + } - if let Some(exit) = exclude_exit { - if h.exit == exit { - return false; - } + if let Some(exit) = exclude_exit { + if h.exit == exit { + return false; } + } - if let Some(cwd) = &exclude_cwd { - if h.cwd.as_str() == cwd.as_str() { - return false; - } + if let Some(cwd) = &exclude_cwd { + if h.cwd.as_str() == cwd.as_str() { + return false; } + } - if let Some(cwd) = &dir { - if h.cwd.as_str() != cwd.as_str() { - return false; - } + if let Some(cwd) = &dir { + if h.cwd.as_str() != cwd.as_str() { + return false; } + } - if let Some(before) = &before { - let before = chrono_english::parse_date_string( - before.as_str(), - Utc::now(), - chrono_english::Dialect::Uk, - ); + if let Some(before) = &before { + let before = chrono_english::parse_date_string( + before.as_str(), + Utc::now(), + chrono_english::Dialect::Uk, + ); - if before.is_err() || h.timestamp.gt(&before.unwrap()) { - return false; - } + if before.is_err() || h.timestamp.gt(&before.unwrap()) { + return false; } + } - if let Some(after) = &after { - let after = chrono_english::parse_date_string( - after.as_str(), - Utc::now(), - chrono_english::Dialect::Uk, - ); + if let Some(after) = &after { + let after = chrono_english::parse_date_string( + after.as_str(), + Utc::now(), + chrono_english::Dialect::Uk, + ); - if after.is_err() || h.timestamp.lt(&after.unwrap()) { - return false; - } + if after.is_err() || h.timestamp.lt(&after.unwrap()) { + return false; } + } - true - }) - .map(std::borrow::ToOwned::to_owned) - .collect(); - - super::history::print_list(&results, human, cmd_only); - } + true + }) + .map(std::borrow::ToOwned::to_owned) + .collect(); + super::history::print_list(&results, list_mode); Ok(()) } |