use std::path::Path;
use tokf::history;
use tokf::tracking;
fn open_history_conn() -> anyhow::Result<rusqlite::Connection> {
let path =
tracking::db_path().ok_or_else(|| anyhow::anyhow!("cannot determine history DB path"))?;
history::open_db(&path)
}
pub fn cmd_history_list(limit: usize, all: bool) -> anyhow::Result<i32> {
let conn = open_history_conn()?;
let project = if all {
None
} else {
Some(history::current_project())
};
let project_ref = project.as_deref();
let entries = history::list_history(&conn, limit, project_ref)?;
if entries.is_empty() {
eprintln!("[tokf] no history entries found");
return Ok(0);
}
for entry in entries {
print_entry_line(&entry, all);
}
Ok(0)
}
pub fn cmd_history_show(id: i64, raw: bool) -> anyhow::Result<i32> {
let conn = open_history_conn()?;
let entry = history::get_history_entry(&conn, id)?;
let Some(entry) = entry else {
eprintln!("[tokf] history entry {id} not found");
return Ok(1);
};
if raw {
print!("{}", entry.raw_output);
return Ok(0);
}
print_entry_detail(&entry);
Ok(0)
}
pub fn cmd_history_last(raw: bool, all: bool) -> anyhow::Result<i32> {
let conn = open_history_conn()?;
let project = if all {
None
} else {
Some(history::current_project())
};
let project_ref = project.as_deref();
let entry = history::get_latest_entry(&conn, project_ref)?;
let Some(entry) = entry else {
eprintln!("[tokf] no history entries found");
return Ok(0);
};
if raw {
print!("{}", entry.raw_output);
return Ok(0);
}
print_entry_detail(&entry);
Ok(0)
}
fn print_entry_detail(entry: &history::HistoryEntry) {
println!("ID: {}", entry.id);
println!("Timestamp: {}", entry.timestamp);
println!("Project: {}", entry.project);
println!("Command: {}", entry.command);
println!(
"Filter: {}",
entry.filter_name.as_deref().unwrap_or("(unknown)")
);
println!("Exit Code: {}", entry.exit_code);
println!("\n--- Raw Output ---");
println!("{}", entry.raw_output);
println!("\n--- Filtered Output ---");
println!("{}", entry.filtered_output);
}
pub fn cmd_history_search(query: &str, limit: usize, all: bool) -> anyhow::Result<i32> {
let conn = open_history_conn()?;
let project = if all {
None
} else {
Some(history::current_project())
};
let project_ref = project.as_deref();
let entries = history::search_history(&conn, query, limit, project_ref)?;
if entries.is_empty() {
eprintln!("[tokf] no matching history entries found");
return Ok(0);
}
for entry in entries {
print_entry_line(&entry, all);
}
Ok(0)
}
pub fn cmd_history_clear(all: bool) -> anyhow::Result<i32> {
let conn = open_history_conn()?;
let project = if all {
None
} else {
Some(history::current_project())
};
let project_ref = project.as_deref();
history::clear_history(&conn, project_ref)?;
if all {
eprintln!("[tokf] history cleared (all projects)");
} else {
eprintln!("[tokf] history cleared for current project");
}
Ok(0)
}
fn print_entry_line(entry: &history::HistoryEntry, show_project: bool) {
let filter = entry.filter_name.as_deref().unwrap_or("(unknown)");
let exit_status = if entry.exit_code == 0 {
"\u{2713}".to_string()
} else {
format!("\u{2717}({})", entry.exit_code)
};
let project_suffix = if show_project {
let basename = Path::new(&entry.project)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(&entry.project);
format!(" ({basename})")
} else {
String::new()
};
println!(
"{} {} {} [{}] {}{}",
entry.id, entry.timestamp, exit_status, filter, entry.command, project_suffix
);
}