diff --git a/src/command/history.rs b/src/command/history.rs index f8a5b27..73be66f 100644 --- a/src/command/history.rs +++ b/src/command/history.rs @@ -3,11 +3,11 @@ use std::env; use eyre::Result; use structopt::StructOpt; -use crate::local::database::{Database, SqliteDatabase}; +use crate::local::database::{Database, Sqlite}; use crate::local::history::History; #[derive(StructOpt)] -pub enum HistoryCmd { +pub enum Cmd { #[structopt( about="begins a new command in the history", aliases=&["s", "st", "sta", "star"], @@ -34,10 +34,10 @@ pub enum HistoryCmd { }, } -impl HistoryCmd { - pub fn run(&self, db: &mut SqliteDatabase) -> Result<()> { +impl Cmd { + pub fn run(&self, db: &mut Sqlite) -> Result<()> { match self { - HistoryCmd::Start { command: words } => { + Self::Start { command: words } => { let command = words.join(" "); let cwd = env::current_dir()?.display().to_string(); @@ -58,7 +58,7 @@ impl HistoryCmd { Ok(()) } - HistoryCmd::End { id, exit } => { + Self::End { id, exit } => { let mut h = db.load(id)?; h.exit = *exit; h.duration = chrono::Utc::now().timestamp_nanos() - h.timestamp; @@ -68,7 +68,7 @@ impl HistoryCmd { Ok(()) } - HistoryCmd::List { distinct } => db.list(*distinct), + Self::List { distinct } => db.list(*distinct), } } } diff --git a/src/command/import.rs b/src/command/import.rs index a593ef6..77db1c8 100644 --- a/src/command/import.rs +++ b/src/command/import.rs @@ -5,13 +5,13 @@ use eyre::{eyre, Result}; use home::home_dir; use structopt::StructOpt; -use crate::local::database::{Database, SqliteDatabase}; +use crate::local::database::{Database, Sqlite}; use crate::local::history::History; -use crate::local::import::ImportZsh; +use crate::local::import::Zsh; use indicatif::ProgressBar; #[derive(StructOpt)] -pub enum ImportCmd { +pub enum Cmd { #[structopt( about="import history for the current shell", aliases=&["a", "au", "aut"], @@ -25,94 +25,89 @@ pub enum ImportCmd { Zsh, } -impl ImportCmd { - fn import_zsh(&self, db: &mut SqliteDatabase) -> Result<()> { - // oh-my-zsh sets HISTFILE=~/.zhistory - // zsh has no default value for this var, but uses ~/.zhistory. - // we could maybe be smarter about this in the future :) - - let histpath = env::var("HISTFILE"); - - let histpath = match histpath { - Ok(p) => PathBuf::from(p), - Err(_) => { - let mut home = home_dir().unwrap(); - home.push(".zhistory"); - - home - } - }; - - if !histpath.exists() { - return Err(eyre!( - "Could not find history file at {}, try setting $HISTFILE", - histpath.to_str().unwrap() - )); - } - - let zsh = ImportZsh::new(histpath.to_str().unwrap())?; - - let progress = ProgressBar::new(zsh.loc); - - let buf_size = 100; - let mut buf = Vec::::with_capacity(buf_size); - - for i in zsh { - match i { - Ok(h) => { - buf.push(h); - } - Err(e) => { - error!("{}", e); - continue; - } - } - - if buf.len() == buf_size { - db.save_bulk(&buf)?; - progress.inc(buf.len() as u64); - - buf = Vec::::with_capacity(buf_size); - } - } - - if buf.len() > 0 { - db.save_bulk(&buf)?; - progress.inc(buf.len() as u64); - } - - progress.finish_with_message("Imported history!"); - - Ok(()) - } - - pub fn run(&self, db: &mut SqliteDatabase) -> Result<()> { +impl Cmd { + pub fn run(&self, db: &mut Sqlite) -> Result<()> { println!(" A'Tuin "); println!("====================="); - println!(" 🌍 "); - println!(" 🐘🐘🐘🐘 "); - println!(" 🐢 "); + println!(" \u{1f30d} "); + println!(" \u{1f418}\u{1f418}\u{1f418}\u{1f418} "); + println!(" \u{1f422} "); println!("====================="); println!("Importing history..."); match self { - ImportCmd::Auto => { - let shell = env::var("SHELL").unwrap_or(String::from("NO_SHELL")); + Self::Auto => { + let shell = env::var("SHELL").unwrap_or_else(|_| String::from("NO_SHELL")); - match shell.as_str() { - "/bin/zsh" => { - println!("Detected ZSH"); - self.import_zsh(db) - } - - _ => { - println!("cannot import {} history", shell); - Ok(()) - } + if shell.as_str() == "/bin/zsh" { + println!("Detected ZSH"); + import_zsh(db) + } else { + println!("cannot import {} history", shell); + Ok(()) } } - ImportCmd::Zsh => self.import_zsh(db), + Self::Zsh => import_zsh(db), } } } + +fn import_zsh(db: &mut Sqlite) -> Result<()> { + // oh-my-zsh sets HISTFILE=~/.zhistory + // zsh has no default value for this var, but uses ~/.zhistory. + // we could maybe be smarter about this in the future :) + + let histpath = env::var("HISTFILE"); + + let histpath = if let Ok(p) = histpath { + PathBuf::from(p) + } else { + let mut home = home_dir().unwrap(); + home.push(".zhistory"); + + home + }; + + if !histpath.exists() { + return Err(eyre!( + "Could not find history file at {}, try setting $HISTFILE", + histpath.to_str().unwrap() + )); + } + + let zsh = Zsh::new(histpath.to_str().unwrap())?; + + let progress = ProgressBar::new(zsh.loc); + + let buf_size = 100; + let mut buf = Vec::::with_capacity(buf_size); + + for i in zsh { + match i { + Ok(h) => { + buf.push(h); + } + Err(e) => { + error!("{}", e); + continue; + } + } + + if buf.len() == buf_size { + db.save_bulk(&buf)?; + progress.inc(buf.len() as u64); + + buf = Vec::::with_capacity(buf_size); + } + } + + if !buf.is_empty() { + db.save_bulk(&buf)?; + progress.inc(buf.len() as u64); + } + + progress.finish_with_message("Imported history!"); + + Ok(()) +} diff --git a/src/command/server.rs b/src/command/server.rs index aee64c0..1ddc73e 100644 --- a/src/command/server.rs +++ b/src/command/server.rs @@ -1,14 +1,15 @@ use eyre::Result; use structopt::StructOpt; -use crate::server::server; +use crate::remote::server; #[derive(StructOpt)] -pub enum ServerCmd { - Start { command: Vec }, +pub enum Cmd { + Start { host: Vec }, } -impl ServerCmd { +#[allow(clippy::unused_self)] // I'll use it later +impl Cmd { pub fn run(&self) -> Result<()> { server::launch(); Ok(()) diff --git a/src/local/database.rs b/src/local/database.rs index 2b014bc..4f99d8a 100644 --- a/src/local/database.rs +++ b/src/local/database.rs @@ -9,7 +9,7 @@ use crate::History; pub trait Database { fn save(&mut self, h: History) -> Result<()>; - fn save_bulk(&mut self, h: &Vec) -> Result<()>; + fn save_bulk(&mut self, h: &[History]) -> Result<()>; fn load(&self, id: &str) -> Result; fn list(&self, distinct: bool) -> Result<()>; fn update(&self, h: History) -> Result<()>; @@ -17,12 +17,12 @@ pub trait Database { // Intended for use on a developer machine and not a sync server. // TODO: implement IntoIterator -pub struct SqliteDatabase { +pub struct Sqlite { conn: Connection, } -impl SqliteDatabase { - pub fn new(path: impl AsRef) -> Result { +impl Sqlite { + pub fn new(path: impl AsRef) -> Result { let path = path.as_ref(); debug!("opening sqlite database at {:?}", path); @@ -39,7 +39,7 @@ impl SqliteDatabase { Self::setup_db(&conn)?; } - Ok(SqliteDatabase { conn }) + Ok(Self { conn }) } fn setup_db(conn: &Connection) -> Result<()> { @@ -65,7 +65,7 @@ impl SqliteDatabase { } } -impl Database for SqliteDatabase { +impl Database for Sqlite { fn save(&mut self, h: History) -> Result<()> { debug!("saving history to sqlite"); let v = vec![h]; @@ -73,7 +73,7 @@ impl Database for SqliteDatabase { self.save_bulk(&v) } - fn save_bulk(&mut self, h: &Vec) -> Result<()> { + fn save_bulk(&mut self, h: &[History]) -> Result<()> { debug!("saving history to sqlite"); let tx = self.conn.transaction()?; @@ -116,7 +116,7 @@ impl Database for SqliteDatabase { where id = ?1", )?; - let iter = stmt.query_map(params![id], |row| { + let mut iter = stmt.query_map(params![id], |row| { Ok(History { id: String::from(id), timestamp: row.get(1)?, @@ -129,11 +129,12 @@ impl Database for SqliteDatabase { }) })?; - for i in iter { - return Ok(i.unwrap()); - } + let history = iter.next().unwrap(); - return Err(eyre!("Failed to fetch history: {}", id)); + match history { + Ok(i) => Ok(i), + Err(e) => Err(eyre!("could not find item: {}", e)), + } } fn update(&self, h: History) -> Result<()> { @@ -152,14 +153,12 @@ impl Database for SqliteDatabase { fn list(&self, distinct: bool) -> Result<()> { debug!("listing history"); - let mut stmt = match distinct { - false => self - .conn - .prepare("SELECT command FROM history order by timestamp asc")?, - - true => self - .conn - .prepare("SELECT distinct command FROM history order by timestamp asc")?, + let mut stmt = if distinct { + self.conn + .prepare("SELECT command FROM history order by timestamp asc")? + } else { + self.conn + .prepare("SELECT distinct command FROM history order by timestamp asc")? }; let history_iter = stmt.query_map(params![], |row| { diff --git a/src/local/history.rs b/src/local/history.rs index 893edbb..e46180b 100644 --- a/src/local/history.rs +++ b/src/local/history.rs @@ -23,10 +23,10 @@ impl History { duration: i64, session: Option, hostname: Option, - ) -> History { + ) -> Self { // get the current session or just generate a random string let env_session = - env::var("ATUIN_SESSION").unwrap_or(Uuid::new_v4().to_simple().to_string()); + env::var("ATUIN_SESSION").unwrap_or_else(|_| Uuid::new_v4().to_simple().to_string()); // best attempt at getting the current hostname, or just unknown let os_hostname = hostname::get().unwrap(); @@ -36,7 +36,7 @@ impl History { let session = session.unwrap_or(env_session); let hostname = hostname.unwrap_or(os_hostname); - History { + Self { id: Uuid::new_v4().to_simple().to_string(), timestamp, command, diff --git a/src/local/import.rs b/src/local/import.rs index e855239..046c74b 100644 --- a/src/local/import.rs +++ b/src/local/import.rs @@ -9,7 +9,7 @@ use eyre::{eyre, Result}; use crate::local::history::History; #[derive(Debug)] -pub struct ImportZsh { +pub struct Zsh { file: BufReader, pub loc: u64, @@ -23,14 +23,14 @@ fn count_lines(path: &str) -> Result { Ok(buf.lines().count()) } -impl ImportZsh { - pub fn new(path: &str) -> Result { +impl Zsh { + pub fn new(path: &str) -> Result { let loc = count_lines(path)?; let file = File::open(path)?; let buf = BufReader::new(file); - Ok(ImportZsh { + Ok(Self { file: buf, loc: loc as u64, }) @@ -50,17 +50,17 @@ fn trim_newline(s: &str) -> String { s } -fn parse_extended(line: String) -> History { +fn parse_extended(line: &str) -> History { let line = line.replacen(": ", "", 2); - let mut split = line.splitn(2, ":"); + let mut split = line.splitn(2, ':'); let time = split.next().unwrap_or("-1"); let time = time .parse::() - .unwrap_or(chrono::Utc::now().timestamp_nanos()); + .unwrap_or_else(|_| chrono::Utc::now().timestamp_nanos()); - let duration = split.next().unwrap(); // might be 0;the command - let mut split = duration.split(";"); + let duration_command = split.next().unwrap(); // might be 0;the command + let mut split = duration_command.split(';'); let duration = split.next().unwrap_or("-1"); // should just be the 0 let duration = duration.parse::().unwrap_or(-1); @@ -79,7 +79,7 @@ fn parse_extended(line: String) -> History { ) } -impl Iterator for ImportZsh { +impl Iterator for Zsh { type Item = Result; fn next(&mut self) -> Option { @@ -94,10 +94,10 @@ impl Iterator for ImportZsh { Err(e) => Some(Err(eyre!("failed to parse line: {}", e))), Ok(_) => { - let extended = line.starts_with(":"); + let extended = line.starts_with(':'); if extended { - Some(Ok(parse_extended(line))) + Some(Ok(parse_extended(line.as_str()))) } else { Some(Ok(History::new( chrono::Utc::now().timestamp_nanos(), // what else? :/ diff --git a/src/main.rs b/src/main.rs index adcced9..022703b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ #![feature(proc_macro_hygiene)] #![feature(decl_macro)] -#![warn(clippy::pedantic)] +#![warn(clippy::pedantic, clippy::nursery)] use std::path::PathBuf; @@ -15,13 +15,13 @@ extern crate log; #[macro_use] extern crate rocket; -use command::{history::HistoryCmd, import::ImportCmd, server::ServerCmd}; -use local::database::SqliteDatabase; +use command::{history, import, server}; +use local::database::Sqlite; use local::history::History; mod command; mod local; -mod server; +mod remote; #[derive(StructOpt)] #[structopt( @@ -43,13 +43,13 @@ enum AtuinCmd { about="manipulate shell history", aliases=&["h", "hi", "his", "hist", "histo", "histor"], )] - History(HistoryCmd), + History(history::Cmd), #[structopt(about = "import shell history from file")] - Import(ImportCmd), + Import(import::Cmd), #[structopt(about = "start an atuin server")] - Server(ServerCmd), + Server(server::Cmd), #[structopt(about = "generates a UUID")] Uuid, @@ -57,24 +57,22 @@ enum AtuinCmd { impl Atuin { fn run(self) -> Result<()> { - let db_path = match self.db { - Some(db_path) => { - let path = db_path - .to_str() - .ok_or(eyre!("path {:?} was not valid UTF-8", db_path))?; - let path = shellexpand::full(path)?; - PathBuf::from(path.as_ref()) - } - None => { - let project_dirs = ProjectDirs::from("com", "elliehuxtable", "atuin").ok_or( - eyre!("could not determine db file location\nspecify one using the --db flag"), - )?; - let root = project_dirs.data_dir(); - root.join("history.db") - } + let db_path = if let Some(db_path) = self.db { + let path = db_path + .to_str() + .ok_or_else(|| eyre!("path {:?} was not valid UTF-8", db_path))?; + let path = shellexpand::full(path)?; + PathBuf::from(path.as_ref()) + } else { + let project_dirs = + ProjectDirs::from("com", "elliehuxtable", "atuin").ok_or_else(|| { + eyre!("could not determine db file location\nspecify one using the --db flag") + })?; + let root = project_dirs.data_dir(); + root.join("history.db") }; - let mut db = SqliteDatabase::new(db_path)?; + let mut db = Sqlite::new(db_path)?; match self.atuin { AtuinCmd::History(history) => history.run(&mut db), diff --git a/src/server/mod.rs b/src/remote/mod.rs similarity index 100% rename from src/server/mod.rs rename to src/remote/mod.rs diff --git a/src/server/server.rs b/src/remote/server.rs similarity index 76% rename from src/server/server.rs rename to src/remote/server.rs index 82acbe2..bc1dc2b 100644 --- a/src/server/server.rs +++ b/src/remote/server.rs @@ -1,5 +1,5 @@ #[get("/")] -fn index() -> &'static str { +const fn index() -> &'static str { "Hello, world!" }