mod args;
mod handlers;
pub use args::*;
use handlers::*;
use crate::config::Config;
use crate::error::Result;
use crate::history::HistoryManager;
use crate::history_db::HistoryManagerDb;
use crate::search::SearchEngine;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
use tracing::debug;
#[derive(Parser)]
#[command(name = "zam")]
#[command(about = "Enhanced shell history manager with sensitive data redaction")]
#[command(version, long_about = None)]
pub struct Cli {
#[arg(short, long, global = true)]
pub config: Option<PathBuf>,
#[arg(short, long, global = true)]
pub verbose: bool,
#[arg(short, long, global = true)]
pub quiet: bool,
#[arg(long, global = true)]
pub no_color: bool,
#[arg(long, global = true)]
pub use_file: bool,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
Log(LogArgs),
Search(SearchArgs),
Import(ImportArgs),
Export(ExportArgs),
Stats(StatsArgs),
Clear(ClearArgs),
Config(ConfigArgs),
Fzf(FzfArgs),
Shell(ShellArgs),
Recent(RecentArgs),
Frequent(FrequentArgs),
Validate(ValidateArgs),
Status,
Merge(MergeArgs),
Tokens(TokensArgs),
Hosts(HostsArgs),
Sessions(SessionsArgs),
Alias(AliasArgs),
EndSession(EndSessionArgs),
Auth(AuthArgs),
Vacuum(VacuumArgs),
Tui,
}
pub(crate) enum HistoryBackend {
File(HistoryManager),
Database(HistoryManagerDb),
}
pub struct CliApp {
pub config: Config,
pub(crate) backend: HistoryBackend,
pub search_engine: SearchEngine,
pub verbose: bool,
pub quiet: bool,
#[allow(dead_code)]
pub no_color: bool,
}
impl CliApp {
pub fn new(cli: &Cli) -> Result<Self> {
let config = if let Some(config_path) = &cli.config {
Config::load_from_path(config_path)?
} else {
Config::load().unwrap_or_else(|_| Config::default())
};
let backend = if cli.use_file {
HistoryBackend::File(HistoryManager::new(config.clone())?)
} else {
HistoryBackend::Database(HistoryManagerDb::new(config.clone())?)
};
let search_engine = SearchEngine::with_config(
config.search.fuzzy_search,
config.search.case_sensitive,
config.search.include_directory,
config.search.include_timestamps,
config.search.max_results,
config.search.highlight_matches,
);
Ok(Self {
config,
backend,
search_engine,
verbose: cli.verbose,
quiet: cli.quiet,
no_color: cli.no_color,
})
}
pub fn run(&mut self, command: &Commands) -> Result<()> {
match &self.backend {
HistoryBackend::File(_) => {
debug!("Using file-based backend");
}
HistoryBackend::Database(_) => {
debug!("Using SQLite database backend");
}
}
match command {
Commands::Log(args) => handle_log(self, args),
Commands::Search(args) => handle_search(self, args),
Commands::Import(args) => handle_import(self, args),
Commands::Export(args) => handle_export(self, args),
Commands::Stats(args) => handle_stats(self, args),
Commands::Clear(args) => handle_clear(self, args),
Commands::Config(args) => handle_config(self, args),
Commands::Fzf(args) => handle_fzf(self, args),
Commands::Shell(args) => handle_shell(self, args),
Commands::Recent(args) => handle_recent(self, args),
Commands::Frequent(args) => handle_frequent(self, args),
Commands::Validate(args) => handle_validate(self, args),
Commands::Status => handle_status(self),
Commands::Merge(args) => handle_merge(self, args),
Commands::Tokens(args) => handle_tokens(self, args),
Commands::Hosts(args) => handle_hosts(self, args),
Commands::Sessions(args) => handle_sessions(self, args),
Commands::Alias(args) => handle_alias(self, args),
Commands::EndSession(args) => handle_end_session(self, args),
Commands::Auth(args) => handle_auth(self, args),
Commands::Vacuum(args) => handle_vacuum(self, args),
Commands::Tui => handle_tui(self),
}
}
pub fn verbose_println(&self, message: &str) {
if self.verbose && !self.quiet {
eprintln!("[verbose] {}", message);
}
}
pub fn provider(&self) -> &dyn crate::backend::HistoryProvider {
match &self.backend {
HistoryBackend::File(mgr) => mgr,
HistoryBackend::Database(mgr) => mgr,
}
}
pub fn provider_mut(&mut self) -> &mut dyn crate::backend::HistoryProvider {
match &mut self.backend {
HistoryBackend::File(mgr) => mgr,
HistoryBackend::Database(mgr) => mgr,
}
}
}
pub fn run() -> Result<()> {
let cli = Cli::parse();
let mut app = CliApp::new(&cli)?;
app.run(&cli.command)
}