diff --git a/src/command/client.rs b/src/command/client.rs new file mode 100644 index 0000000..fd65345 --- /dev/null +++ b/src/command/client.rs @@ -0,0 +1,133 @@ +use clap::CommandFactory; +use clap::Subcommand; +use clap_complete::Shell; +use clap_complete::{generate, generate_to}; +use eyre::{Result, WrapErr}; + +use atuin_client::database::Sqlite; +use atuin_client::settings::Settings; +use atuin_common::utils::uuid_v4; + +mod event; +mod history; +mod import; +mod init; +mod login; +mod logout; +mod register; +mod search; +mod stats; +mod sync; +use std::path::PathBuf; + +#[derive(Subcommand)] +#[clap(infer_subcommands = true)] +pub enum Cmd { + /// Manipulate shell history + #[clap(subcommand)] + History(history::Cmd), + + /// Import shell history from file + #[clap(subcommand)] + Import(import::Cmd), + + /// Calculate statistics for your history + #[clap(subcommand)] + Stats(stats::Cmd), + + /// Output shell setup + #[clap(subcommand)] + Init(init::Cmd), + + /// Generate a UUID + Uuid, + + /// Interactive history search + Search(search::Cmd), + + /// Sync with the configured server + Sync { + /// Force re-download everything + #[clap(long, short)] + force: bool, + }, + + /// Login to the configured server + Login(login::Cmd), + + /// Log out + Logout, + + /// Register with the configured server + Register(register::Cmd), + + /// Print the encryption key for transfer to another machine + Key, + + /// Generate shell completions + GenCompletions { + /// Set the shell for generating completions + #[clap(long, short)] + shell: Shell, + + /// Set the output directory + #[clap(long, short)] + out_dir: Option, + }, +} + +impl Cmd { + pub async fn run(self) -> Result<()> { + pretty_env_logger::init(); + + let settings = Settings::new().wrap_err("could not load client settings")?; + + let db_path = PathBuf::from(settings.db_path.as_str()); + let mut db = Sqlite::new(db_path).await?; + + match self { + Self::History(history) => history.run(&settings, &mut db).await, + Self::Import(import) => import.run(&mut db).await, + Self::Stats(stats) => stats.run(&mut db, &settings).await, + Self::Init(init) => { + init.run(); + Ok(()) + } + Self::Search(search) => search.run(&mut db, &settings).await, + Self::Sync { force } => sync::run(&settings, force, &mut db).await, + Self::Login(l) => l.run(&settings).await, + Self::Logout => logout::run(), + Self::Register(r) => r.run(&settings).await, + Self::Key => { + use atuin_client::encryption::{encode_key, load_key}; + let key = load_key(&settings).wrap_err("could not load encryption key")?; + let encode = encode_key(key).wrap_err("could not encode encryption key")?; + println!("{}", encode); + Ok(()) + } + Self::Uuid => { + println!("{}", uuid_v4()); + Ok(()) + } + Self::GenCompletions { shell, out_dir } => { + let mut cli = crate::Atuin::command(); + + match out_dir { + Some(out_dir) => { + generate_to(shell, &mut cli, env!("CARGO_PKG_NAME"), &out_dir)?; + } + None => { + generate( + shell, + &mut cli, + env!("CARGO_PKG_NAME"), + &mut std::io::stdout(), + ); + } + } + + Ok(()) + } + } + } +} diff --git a/src/command/event.rs b/src/command/client/event.rs similarity index 100% rename from src/command/event.rs rename to src/command/client/event.rs diff --git a/src/command/history.rs b/src/command/client/history.rs similarity index 100% rename from src/command/history.rs rename to src/command/client/history.rs diff --git a/src/command/import.rs b/src/command/client/import.rs similarity index 100% rename from src/command/import.rs rename to src/command/client/import.rs diff --git a/src/command/init.rs b/src/command/client/init.rs similarity index 73% rename from src/command/init.rs rename to src/command/client/init.rs index 37453f9..a2c6378 100644 --- a/src/command/init.rs +++ b/src/command/client/init.rs @@ -11,17 +11,17 @@ pub enum Cmd { } fn init_zsh() { - let full = include_str!("../shell/atuin.zsh"); + let full = include_str!("../../shell/atuin.zsh"); println!("{}", full); } fn init_bash() { - let full = include_str!("../shell/atuin.bash"); + let full = include_str!("../../shell/atuin.bash"); println!("{}", full); } fn init_fish() { - let full = include_str!("../shell/atuin.fish"); + let full = include_str!("../../shell/atuin.fish"); println!("{}", full); } diff --git a/src/command/login.rs b/src/command/client/login.rs similarity index 100% rename from src/command/login.rs rename to src/command/client/login.rs diff --git a/src/command/logout.rs b/src/command/client/logout.rs similarity index 60% rename from src/command/logout.rs rename to src/command/client/logout.rs index 26d689c..a7e9541 100644 --- a/src/command/logout.rs +++ b/src/command/client/logout.rs @@ -1,12 +1,15 @@ +use eyre::{Context, Result}; use fs_err::remove_file; -pub fn run() { +pub fn run() -> Result<()> { let session_path = atuin_common::utils::data_dir().join("session"); if session_path.exists() { - remove_file(session_path.as_path()).expect("Failed to remove session file"); + remove_file(session_path.as_path()).context("Failed to remove session file")?; println!("You have logged out!"); } else { println!("You are not logged in"); } + + Ok(()) } diff --git a/src/command/register.rs b/src/command/client/register.rs similarity index 88% rename from src/command/register.rs rename to src/command/client/register.rs index 46f4a65..2c60a2e 100644 --- a/src/command/register.rs +++ b/src/command/client/register.rs @@ -19,6 +19,12 @@ pub struct Cmd { pub password: Option, } +impl Cmd { + pub async fn run(self, settings: &Settings) -> Result<()> { + run(settings, &self.username, &self.email, &self.password).await + } +} + pub async fn run( settings: &Settings, username: &Option, diff --git a/src/command/search.rs b/src/command/client/search.rs similarity index 92% rename from src/command/search.rs rename to src/command/client/search.rs index a28b154..a1dc5aa 100644 --- a/src/command/search.rs +++ b/src/command/client/search.rs @@ -1,7 +1,7 @@ use chrono::Utc; +use clap::Parser; use eyre::Result; use std::{io::stdout, ops::Sub, time::Duration}; - use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen}; use tui::{ backend::{Backend, TermionBackend}, @@ -19,10 +19,75 @@ use atuin_client::{ settings::{SearchMode, Settings}, }; -use crate::command::event::{Event, Events}; +use super::event::{Event, Events}; const VERSION: &str = env!("CARGO_PKG_VERSION"); +#[derive(Parser)] +pub struct Cmd { + /// Filter search result by directory + #[clap(long, short)] + cwd: Option, + + /// Exclude directory from results + #[clap(long = "exclude-cwd")] + exclude_cwd: Option, + + /// Filter search result by exit code + #[clap(long, short)] + exit: Option, + + /// Exclude results with this exit code + #[clap(long = "exclude-exit")] + exclude_exit: Option, + + /// Only include results added before this date + #[clap(long, short)] + before: Option, + + /// Only include results after this date + #[clap(long)] + after: Option, + + /// Open interactive search UI + #[clap(long, short)] + interactive: bool, + + /// Use human-readable formatting for time + #[clap(long)] + human: bool, + + query: Vec, + + /// Show only the text of the command + #[clap(long)] + cmd_only: bool, +} + +impl Cmd { + pub async fn run( + self, + 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 + } +} + struct State { input: String, diff --git a/src/command/stats.rs b/src/command/client/stats.rs similarity index 100% rename from src/command/stats.rs rename to src/command/client/stats.rs diff --git a/src/command/sync.rs b/src/command/client/sync.rs similarity index 100% rename from src/command/sync.rs rename to src/command/client/sync.rs diff --git a/src/command/mod.rs b/src/command/mod.rs index 8463421..3a3ed39 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -1,212 +1,25 @@ -use std::path::PathBuf; - -use clap::CommandFactory; use clap::Subcommand; -use clap_complete::Shell; -use clap_complete::{generate, generate_to}; -use eyre::{Result, WrapErr}; +use eyre::Result; -use atuin_client::database::Sqlite; -use atuin_client::settings::Settings as ClientSettings; -use atuin_common::utils::uuid_v4; -use atuin_server::settings::Settings as ServerSettings; - -mod event; -mod history; -mod import; -mod init; -mod login; -mod logout; -mod register; -mod search; +mod client; mod server; -mod stats; -mod sync; #[derive(Subcommand)] #[clap(infer_subcommands = true)] pub enum AtuinCmd { - /// Manipulate shell history - #[clap(subcommand)] - History(history::Cmd), - - /// Import shell history from file - #[clap(subcommand)] - Import(import::Cmd), + #[clap(flatten)] + Client(client::Cmd), /// Start an atuin server #[clap(subcommand)] Server(server::Cmd), - - /// Calculate statistics for your history - #[clap(subcommand)] - Stats(stats::Cmd), - - /// Output shell setup - #[clap(subcommand)] - Init(init::Cmd), - - /// Generate a UUID - Uuid, - - /// Interactive history search - Search { - /// Filter search result by directory - #[clap(long, short)] - cwd: Option, - - /// Exclude directory from results - #[clap(long = "exclude-cwd")] - exclude_cwd: Option, - - /// Filter search result by exit code - #[clap(long, short)] - exit: Option, - - /// Exclude results with this exit code - #[clap(long = "exclude-exit")] - exclude_exit: Option, - - /// Only include results added before this date - #[clap(long, short)] - before: Option, - - /// Only include results after this date - #[clap(long)] - after: Option, - - /// Open interactive search UI - #[clap(long, short)] - interactive: bool, - - /// Use human-readable formatting for time - #[clap(long)] - human: bool, - - query: Vec, - - /// Show only the text of the command - #[clap(long)] - cmd_only: bool, - }, - - /// Sync with the configured server - Sync { - /// Force re-download everything - #[clap(long, short)] - force: bool, - }, - - /// Login to the configured server - Login(login::Cmd), - - /// Log out - Logout, - - /// Register with the configured server - Register(register::Cmd), - - /// Print the encryption key for transfer to another machine - Key, - - /// Generate shell completions - GenCompletions { - /// Set the shell for generating completions - #[clap(long, short)] - shell: Shell, - - /// Set the output directory - #[clap(long, short)] - out_dir: Option, - }, } impl AtuinCmd { pub async fn run(self) -> Result<()> { - let client_settings = ClientSettings::new().wrap_err("could not load client settings")?; - let server_settings = ServerSettings::new().wrap_err("could not load server settings")?; - - let db_path = PathBuf::from(client_settings.db_path.as_str()); - - let mut db = Sqlite::new(db_path).await?; - match self { - Self::History(history) => history.run(&client_settings, &mut db).await, - Self::Import(import) => import.run(&mut db).await, - Self::Server(server) => server.run(server_settings).await, - Self::Stats(stats) => stats.run(&mut db, &client_settings).await, - Self::Init(init) => { - init.run(); - Ok(()) - } - Self::Search { - cwd, - exit, - interactive, - human, - exclude_exit, - exclude_cwd, - before, - after, - query, - cmd_only, - } => { - search::run( - &client_settings, - cwd, - exit, - interactive, - human, - exclude_exit, - exclude_cwd, - before, - after, - cmd_only, - &query, - &mut db, - ) - .await - } - - Self::Sync { force } => sync::run(&client_settings, force, &mut db).await, - Self::Login(l) => l.run(&client_settings).await, - Self::Logout => { - logout::run(); - Ok(()) - } - Self::Register(r) => { - register::run(&client_settings, &r.username, &r.email, &r.password).await - } - Self::Key => { - use atuin_client::encryption::{encode_key, load_key}; - let key = load_key(&client_settings).wrap_err("could not load encryption key")?; - let encode = encode_key(key).wrap_err("could not encode encryption key")?; - println!("{}", encode); - Ok(()) - } - Self::Uuid => { - println!("{}", uuid_v4()); - Ok(()) - } - Self::GenCompletions { shell, out_dir } => { - let mut cli = crate::Atuin::command(); - - match out_dir { - Some(out_dir) => { - generate_to(shell, &mut cli, env!("CARGO_PKG_NAME"), &out_dir)?; - } - None => { - generate( - shell, - &mut cli, - env!("CARGO_PKG_NAME"), - &mut std::io::stdout(), - ); - } - } - - Ok(()) - } + Self::Client(client) => client.run().await, + Self::Server(server) => server.run().await, } } } diff --git a/src/command/server.rs b/src/command/server.rs index 9d97e92..cd05cac 100644 --- a/src/command/server.rs +++ b/src/command/server.rs @@ -1,5 +1,5 @@ use clap::Parser; -use eyre::Result; +use eyre::{Context, Result}; use atuin_server::launch; use atuin_server::settings::Settings; @@ -20,7 +20,11 @@ pub enum Cmd { } impl Cmd { - pub async fn run(&self, settings: Settings) -> Result<()> { + pub async fn run(self) -> Result<()> { + pretty_env_logger::init(); + + let settings = Settings::new().wrap_err("could not load server settings")?; + match self { Self::Start { host, port } => { let host = host diff --git a/src/main.rs b/src/main.rs index 2fee879..e5c81bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,5 @@ impl Atuin { #[tokio::main] async fn main() -> Result<()> { - pretty_env_logger::init(); - Atuin::parse().run().await }