diff --git a/Cargo.toml b/Cargo.toml index 7083cd9..ed29050 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ itertools = "0.11" log = "0.4" reqwest = { version = "0.11", features = ["blocking", "json"] } serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/src/main.rs b/src/main.rs index 358fd22..994eb11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ use clap::{Parser, Subcommand}; use itertools::Itertools; use log::{debug, error, info}; +use serde::{Serialize, Deserialize}; use std::env; +use std::fs; +use std::io::Read; use std::io::Write; use std::process::Stdio; use std::{process, collections::HashMap}; @@ -29,12 +32,17 @@ struct Cli { #[derive(Subcommand)] enum CliCommands { + // Show the details of the item Select { - // Show the details of the item title: Option, }, } +#[derive(Serialize, Deserialize)] +struct History { + history: Vec, +} + fn main() { env_logger::init(); @@ -50,6 +58,64 @@ fn main() { } +fn get_history() -> Vec { + let file_path = "/tmp/bw-menu-history.json"; // TODO: use XDG paths + + let read_file = fs::OpenOptions::new() + .read(true) + .open(file_path); + + let json_str = match read_file { + Ok(mut file) => { + let mut content = String::new(); + file.read_to_string(&mut content).expect("Failed to read file"); + content + } + Err(_) => String::new(), // Return an empty string if the file cannot be opened + }; + + // Deserialize JSON string back into a Rust struct + let history: History = serde_json::from_str(&json_str).unwrap_or_else(|_| { + // Return a default Person with an empty history if deserialization fails + History { + history: Vec::new(), + } + }); + + history.history +} + + +fn save_history(entries: Vec) { + let history = History { + history: entries, + }; + let json_str = serde_json::to_string(&history).expect("Failed to serialize to JSON!"); + + let file_path = "/tmp/bw-menu-history.json"; // TODO: use XDG paths + + // Write the JSON string to a file, overwriting it if it already exists + let mut file = fs::OpenOptions::new() + .write(true) + .truncate(true) // Ensure the file is truncated before writing + .create(true) + .open(file_path) + .expect("Failed to open file for writing!"); + + file.write_all(json_str.as_bytes()).expect("Failed to write to file!"); +} + +const MAX_HISTORY_LEN: usize = 5; // TODO: make configurable +fn add_history(entry: &String) { + let mut history = get_history(); + history.insert(0, entry.to_string()); + if history.len() > MAX_HISTORY_LEN { + history = history[0..MAX_HISTORY_LEN].to_vec(); + } + save_history(history); +} + + // the flow is: // 1. bw-menu starts rofi in the script mode // 2. rofi calls bw-menu calls bw-menu to get a list of items to display @@ -104,6 +170,7 @@ fn rofi() { process::exit(1); } }; + add_history(&rofi_info); // 1: Selected an entry if rofi_retv == 1 { info!("copy password");