diff --git a/Cargo.lock b/Cargo.lock index 689d0bf..7eb5b0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,6 +120,8 @@ dependencies = [ "pretty_env_logger", "rocket", "rusqlite", + "serde 1.0.124", + "serde_derive", "shellexpand", "structopt", "uuid", @@ -293,17 +295,17 @@ dependencies = [ [[package]] name = "config" -version = "0.9.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9107d78ed62b3fa5a86e7d18e647abed48cfd8f8fab6c72f4cdb982d196f7e6" +checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", "nom", "rust-ini", - "serde 1.0.123", + "serde 1.0.124", "serde-hjson", "serde_json", - "toml", + "toml 0.5.8", "yaml-rust", ] @@ -314,7 +316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cc80946b3480f421c2f17ed1cb841753a371c7c5104f51d507e13f532c856aa" dependencies = [ "encode_unicode", - "lazy_static 1.4.0", + "lazy_static", "libc", "regex", "terminal_size", @@ -352,7 +354,7 @@ checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" dependencies = [ "autocfg", "cfg-if 1.0.0", - "lazy_static 1.4.0", + "lazy_static", ] [[package]] @@ -675,7 +677,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" dependencies = [ "console", - "lazy_static 1.4.0", + "lazy_static", "number_prefix", "regex", ] @@ -692,18 +694,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lexical-core" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 1.0.0", + "ryu", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.86" @@ -784,12 +793,13 @@ dependencies = [ [[package]] name = "nom" -version = "4.2.3" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ + "lexical-core", "memchr", - "version_check 0.1.5", + "version_check 0.9.2", ] [[package]] @@ -1091,7 +1101,7 @@ dependencies = [ "rocket_http", "state", "time", - "toml", + "toml 0.4.10", "version_check 0.9.2", "yansi", ] @@ -1187,23 +1197,34 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" [[package]] name = "serde" -version = "1.0.123" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" [[package]] name = "serde-hjson" -version = "0.8.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b833c5ad67d52ced5f5938b2980f32a9c1c5ef047f0b4fb3127e7a423c76153" +checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" dependencies = [ - "lazy_static 0.2.11", + "lazy_static", "linked-hash-map 0.3.0", "num-traits 0.1.43", "regex", "serde 0.8.23", ] +[[package]] +name = "serde_derive" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.9", + "syn 1.0.60", +] + [[package]] name = "serde_json" version = "1.0.62" @@ -1212,7 +1233,7 @@ checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" dependencies = [ "itoa", "ryu", - "serde 1.0.123", + "serde 1.0.124", ] [[package]] @@ -1257,6 +1278,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3015a7d0a5fd5105c91c3710d42f9ccf0abfb287d62206484dcc67f9569a6483" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" @@ -1270,7 +1297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" dependencies = [ "clap", - "lazy_static 1.4.0", + "lazy_static", "structopt-derive", ] @@ -1389,7 +1416,16 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" dependencies = [ - "serde 1.0.123", + "serde 1.0.124", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde 1.0.124", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f980444..19bb0d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,9 @@ hostname = "0.3.1" rocket = "0.4.7" chrono-english = "0.1.4" cli-table = "0.4" -config = "0.9" +config = "0.10" +serde_derive = "1.0.124" +serde = "1.0.124" [dependencies.rusqlite] version = "0.24" diff --git a/README.md b/README.md index 5c44041..30e6dd9 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,20 @@ $ atuin stats all +---------------------+-------+ ``` +## Config + +A'tuin is configurable via TOML. The file lives at ` ~/.config/atuin/config.toml`, +and looks like this: + +``` +[local] +dialect = "uk" # or us. sets the date format used by stats +server_address = "https://atuin.elliehuxtable.com/" # the server to sync with + +[local.db] +path = "~/.local/share/atuin/history.db" # the local database for history +``` + ## ...what's with the name? A'tuin is named after "The Great A'tuin", a giant turtle from Terry Pratchett's diff --git a/src/command/mod.rs b/src/command/mod.rs index 0952540..c74b138 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -3,6 +3,7 @@ use structopt::StructOpt; use uuid::Uuid; use crate::local::database::Database; +use crate::settings::Settings; mod history; mod import; @@ -39,12 +40,12 @@ pub fn uuid_v4() -> String { } impl AtuinCmd { - pub fn run(self, db: &mut impl Database) -> Result<()> { + pub fn run(self, db: &mut impl Database, settings: &Settings) -> Result<()> { match self { Self::History(history) => history.run(db), Self::Import(import) => import.run(db), Self::Server(server) => server.run(), - Self::Stats(stats) => stats.run(db), + Self::Stats(stats) => stats.run(db, settings), Self::Init => init::init(), Self::Uuid => { diff --git a/src/command/stats.rs b/src/command/stats.rs index 19079b8..694484b 100644 --- a/src/command/stats.rs +++ b/src/command/stats.rs @@ -10,6 +10,7 @@ use structopt::StructOpt; use crate::local::database::Database; use crate::local::history::History; +use crate::settings::Settings; #[derive(StructOpt)] pub enum Cmd { @@ -70,7 +71,7 @@ fn compute_stats(history: &[History]) -> Result<()> { } impl Cmd { - pub fn run(&self, db: &mut impl Database) -> Result<()> { + pub fn run(&self, db: &mut impl Database, settings: &Settings) -> Result<()> { match self { Self::Day { words } => { let words = if words.is_empty() { @@ -79,7 +80,10 @@ impl Cmd { words.join(" ") }; - let start = parse_date_string(&words, Local::now(), Dialect::Us)?; + let start = match settings.local.dialect.to_lowercase().as_str() { + "uk" => parse_date_string(&words, Local::now(), Dialect::Uk)?, + _ => parse_date_string(&words, Local::now(), Dialect::Us)?, + }; let end = start + Duration::days(1); let history = db.range(start.into(), end.into())?; diff --git a/src/main.rs b/src/main.rs index 380df59..78e1073 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,10 @@ -#![feature(str_split_once)] #![feature(proc_macro_hygiene)] #![feature(decl_macro)] #![warn(clippy::pedantic, clippy::nursery)] +#![allow(clippy::use_self)] // not 100% reliable use std::path::PathBuf; -use directories::ProjectDirs; use eyre::{eyre, Result}; use structopt::StructOpt; @@ -15,12 +14,17 @@ extern crate log; #[macro_use] extern crate rocket; +#[macro_use] +extern crate serde_derive; + use command::AtuinCmd; use local::database::Sqlite; +use settings::Settings; mod command; mod local; mod remote; +mod settings; #[derive(StructOpt)] #[structopt( @@ -38,6 +42,8 @@ struct Atuin { impl Atuin { fn run(self) -> Result<()> { + let settings = Settings::new()?; + let db_path = if let Some(db_path) = self.db { let path = db_path .to_str() @@ -45,17 +51,12 @@ impl Atuin { let path = shellexpand::full(path)?; PathBuf::from(path.as_ref()) } else { - ProjectDirs::from("com", "elliehuxtable", "atuin") - .ok_or_else(|| { - eyre!("could not determine db file location\nspecify one using the --db flag") - })? - .data_dir() - .join("history.db") + PathBuf::from(settings.local.db.path.as_str()) }; let mut db = Sqlite::new(db_path)?; - self.atuin.run(&mut db) + self.atuin.run(&mut db, &settings) } } diff --git a/src/settings.rs b/src/settings.rs new file mode 100644 index 0000000..a4c9f8d --- /dev/null +++ b/src/settings.rs @@ -0,0 +1,64 @@ +use std::path::PathBuf; + +use config::{Config, File}; +use directories::ProjectDirs; +use eyre::{eyre, Result}; +use std::fs; + +#[derive(Debug, Deserialize)] +pub struct LocalDatabase { + pub path: String, +} + +#[derive(Debug, Deserialize)] +pub struct Local { + pub server_address: String, + pub dialect: String, + pub db: LocalDatabase, +} + +#[derive(Debug, Deserialize)] +pub struct Settings { + pub local: Local, +} + +impl Settings { + pub fn new() -> Result { + let config_dir = ProjectDirs::from("com", "elliehuxtable", "atuin").unwrap(); + let config_dir = config_dir.config_dir(); + + fs::create_dir_all(config_dir)?; + + let mut config_file = PathBuf::new(); + config_file.push(config_dir); + config_file.push("config.toml"); + let config_file = config_file.as_path(); + + // create the config file if it does not exist + + let mut s = Config::new(); + + let db_path = ProjectDirs::from("com", "elliehuxtable", "atuin") + .ok_or_else(|| { + eyre!("could not determine db file location\nspecify one using the --db flag") + })? + .data_dir() + .join("history.db"); + + s.set_default("local.server_address", "https://atuin.elliehuxtable.com")?; + s.set_default("local.dialect", "us")?; + s.set_default("local.db.path", db_path.to_str())?; + + if config_file.exists() { + s.merge(File::with_name(config_file.to_str().unwrap()))?; + } + + // all paths should be expanded + let db_path = s.get_str("local.db.path")?; + let db_path = shellexpand::full(db_path.as_str())?; + s.set("local.db.path", db_path.to_string())?; + + s.try_into() + .map_err(|e| eyre!("failed to deserialize: {}", e)) + } +}