use std::{ fs::File, io::{BufRead, BufReader, ErrorKind}, path::PathBuf, }; use clap::Args; use directories::ProjectDirs; use crate::{session::SessionWriter, Session}; #[derive(Debug, Clone, Args)] #[group(skip)] pub struct History { /// Update the history file from the current sessions #[arg(short, long)] pub update: bool, /// path to history file [default: $XDG_DATA_HOME/sshr/history] #[arg(short = 'f', long = "history_file")] path: Option, } impl History { pub fn new(History { update, path }: History) -> Self { Self { path: path.or_else(History::default_path), update, } } pub fn read(&self) -> Result, std::io::Error> { let Some(path) = &self.path() else { tracing::warn!(?self.path, "History file does not exist"); return Ok(Vec::new()); }; let sessions = BufReader::new(File::open(path)?) .lines() .flatten() .flat_map(|item| ron::from_str(&item)) .collect(); Ok(sessions) } fn default_path() -> Option { ProjectDirs::from("", "", env!("CARGO_CRATE_NAME"))? .state_dir()? .join("history") .into() } pub fn path(&self) -> Option { self.path.clone().or_else(History::default_path) } } impl SessionWriter for History { type Writer = File; type Error = ron::Error; fn format(&self, session: &Session) -> Result { ron::to_string(session) } fn filter(&self, session: &Session) -> bool { self.update && !matches!(session.state, crate::State::Discovered) } fn writer(&self) -> Result { match &self.path { Some(path) => File::create(path), None => Err(std::io::Error::from(ErrorKind::NotFound)), } } }