From 1cac4fc187aecc20f529667ae44bacd6dfae6177 Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Thu, 18 Apr 2024 08:21:43 -0500 Subject: [PATCH] Maybe I can mutex now --- Cargo.lock | 271 ++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- src/cli.rs | 4 + src/config.rs | 78 ++++++++++++++ src/main.rs | 5 +- src/prelude.rs | 1 + src/tasks.rs | 23 ++++- src/tui.rs | 29 ++---- 8 files changed, 386 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a90346a..0d197f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,6 +339,9 @@ name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] [[package]] name = "block-buffer" @@ -554,6 +557,55 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +dependencies = [ + "async-trait", + "convert_case", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -637,6 +689,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -678,6 +736,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + [[package]] name = "either" version = "1.9.0" @@ -1028,6 +1095,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "hashbrown" version = "0.14.3" @@ -1197,7 +1270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.3", ] [[package]] @@ -1259,6 +1332,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "keyring" version = "2.3.2" @@ -1285,6 +1369,12 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-keyutils" version = "0.2.4" @@ -1329,7 +1419,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ - "hashbrown", + "hashbrown 0.14.3", ] [[package]] @@ -1387,6 +1477,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -1438,6 +1534,16 @@ dependencies = [ "memoffset 0.7.1", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1604,6 +1710,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-multimap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" +dependencies = [ + "dlv-list", + "hashbrown 0.13.2", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -1686,12 +1802,63 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "pest_meta" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1770,7 +1937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", ] [[package]] @@ -1846,6 +2013,9 @@ name = "redact" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7301863b5e5486c9f18320ccc1aedce9a5fdf3056ae89fa021933d6f054430f" +dependencies = [ + "serde", +] [[package]] name = "redox_syscall" @@ -2016,6 +2186,28 @@ dependencies = [ "rand", ] +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags 2.5.0", + "serde", + "serde_derive", +] + +[[package]] +name = "rust-ini" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -2169,6 +2361,15 @@ dependencies = [ "syn 2.0.53", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2423,6 +2624,7 @@ dependencies = [ "clap", "clap_derive", "color-eyre", + "config", "crossterm", "futures", "keyring", @@ -2518,6 +2720,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2599,11 +2810,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.9", +] + [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -2613,7 +2839,20 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.6", ] [[package]] @@ -2717,6 +2956,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "uds_windows" version = "1.1.0" @@ -3089,6 +3334,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -3115,6 +3369,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zbus" version = "3.15.2" diff --git a/Cargo.toml b/Cargo.toml index dc9bd1b..a81f22c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,12 @@ chrono-humanize = "0.2.3" clap = { version = "4.5.4", features = ["derive"] } clap_derive = "4.5.4" color-eyre = "0.6.3" +config = "0.14.0" crossterm = "0.27.0" futures = "0.3.30" keyring = "2.3.2" ratatui = "0.26.2" -redact = "0.1.9" +redact = { version = "0.1.9", features = ["serde"] } regex = "1.10.3" reqwest = { version = "0.11.26", features = ["json", "socks"] } reqwest-middleware = "0.2.5" diff --git a/src/cli.rs b/src/cli.rs index eba9572..492655f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -26,6 +26,10 @@ pub struct Cli { /// The directory that data is stored in (defaults to $XDG_DATA_HOME/taskr/data) #[arg(long, default_value = None)] pub data_directory: Option, + + /// The configuration file to load (defaults to $XDG_CONFIG_HOME/taskr/config.toml) + #[arg(long, default_value = None)] + pub data_directory: Option, } #[derive(Subcommand)] diff --git a/src/config.rs b/src/config.rs index 8b13789..4951b2b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1 +1,79 @@ +use crate::prelude::*; +use std::{collections::HashMap, fmt::Debug}; + +use config::{builder::DefaultState, Config as CConfig, ConfigBuilder}; +use serde::{Deserialize, Serialize}; + +const CURRENT_VERSION: u64 = 1; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Gitlab { + pub url: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Jira { + pub url: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Config { + pub version: u64, + pub gitlab: Option, + pub jira: Option, +} + +const CONFIG_FILE_PATH: &str = "./conf.toml"; + +impl Config { + pub fn load() -> Result { + let c = Self::builder()?.build()?; + Ok(c.try_deserialize()?) + } + + pub fn builder() -> Result> { + let p = std::path::PathBuf::from(CONFIG_FILE_PATH); + if !p.exists() { + // TODO: this won't output since tracing is not setup before we load + // config, so we need something like + // https://docs.rs/tracing-capture/latest/tracing_capture/ + // to capture trace events to be output _after_ configuration has + // loaded and the "actual" subscriber is setup + warn!( + "Config file '{}' doesn't exist, so defaults will likely be loaded!", + p.display(), + ); + } + + // TODO: log whether or not we were able to load conf.toml? + Ok(Self::default_builder() + .map_err(anyhow::Error::from)? + .add_source(config::File::from(p).required(false)) + .add_source(config::Environment::with_prefix("taskr").separator("__"))) + } + + pub fn default_builder() -> Result> { + Ok(CConfig::builder() + .set_default("version", CURRENT_VERSION)? + .set_default("jira", Option::>::None)? + .set_default("gitlab", Option::>::None)?) + } + + #[allow(dead_code)] + pub fn defaults() -> Result { + let c = Self::default_builder()?.build()?; + Ok(c.try_deserialize()?) + } +} + +#[cfg(test)] +mod tests { + use crate::config::*; + + #[test] + fn defaults_deserialize_to_valid_config() -> Result<()> { + println!("Defaults: {:?}", Config::defaults()?); + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index cff9a0d..b00d950 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,11 +12,14 @@ mod task; mod tasks; mod tui; +use config::Config; + use crate::cli::{Cli, Commands}; use crate::prelude::*; #[tokio::main] async fn main() -> Result<()> { + let conf = Config::load()?; let cli = Cli::new(); // this guard causes logs to be flushed when dropped (which is at the end of @@ -24,7 +27,7 @@ async fn main() -> Result<()> { // https://docs.rs/tracing-appender/latest/tracing_appender/non_blocking/struct.WorkerGuard.html let _log_guard = observe::setup_logging(cli.logs_directory, cli.tracing_env_filter)?; - let tasks = Arc::new(crate::tasks::Tasks::try_new(cli.data_directory)?); + let tasks = Arc::new(crate::tasks::Tasks::try_new(conf, cli.data_directory)?); let result = match cli.command { Commands::Ui(args) => args.run(tasks).await, diff --git a/src/prelude.rs b/src/prelude.rs index d051161..d8b2816 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,6 @@ #![allow(unused_imports)] +pub use crate::config::Config; pub use crate::result::Result; pub use regex::Regex; pub use std::sync::Arc; diff --git a/src/tasks.rs b/src/tasks.rs index 609af2b..2223f47 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -19,7 +19,9 @@ use crate::{gitlab::GitLab, jira::Jira, result::Result}; pub type Db = sled::Db; pub struct Tasks { + config: Config, data_dir: PathBuf, + gitlab: OnceLock, jira: OnceLock, @@ -29,7 +31,7 @@ pub struct Tasks { const MY_DONE_STATUS_CATEGORY_KEY: &str = "done"; impl Tasks { - pub fn try_new(data_dir: Option) -> Result + pub fn try_new(config: Config, data_dir: Option) -> Result where D: AsRef, { @@ -53,6 +55,7 @@ impl Tasks { let data_dir: PathBuf = data_dir.to_owned(); Ok(Self { + config, gitlab, jira, data_dir, @@ -105,7 +108,14 @@ impl Tasks { token } }; - let result = GitLab::try_new("https://git.hq.bill.com/api/v4", &gl_token)?; + let result = GitLab::try_new( + &format!( + "{}/api/v4", + self.config + .require_or_prompt_and_set::("gitlab.url")? + ), + &gl_token, + )?; let _ = self.gitlab.set(result); Ok(self.gitlab.get().unwrap()) // TODO: ensure the token works? @@ -148,7 +158,14 @@ impl Tasks { } }; - let result = Jira::try_new("https://billcom.atlassian.net", &jira_token)?; + let result = Jira::try_new( + &format!( + "{}", + self.config + .require_or_prompt_and_set::("jira.url")? + ), + &jira_token, + )?; let _ = self.jira.set(result); Ok(self.jira.get().unwrap()) // TODO: ensure the token works? diff --git a/src/tui.rs b/src/tui.rs index b683a52..69f89ff 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -13,27 +13,11 @@ use ratatui::{ use signal_hook::consts::*; use signal_hook_tokio::Signals; use std::io::stdout; -use tokio::task::JoinHandle; pub struct Tui { tasks: SharedTasks, } -async fn handle_signals(mut signals: Signals, channel: ) { - while let Some(signal) = signals.next().await { - match signal { - SIGHUP => { - // Reload configuration - // Reopen the log file - } - SIGTERM | SIGINT | SIGQUIT => { - // Shutdown the system; - }, - _ => unreachable!(), - } - } -} - impl Tui { pub fn new(tasks: SharedTasks) -> Self { Self { tasks } @@ -49,8 +33,17 @@ impl Tui { let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; terminal.clear()?; let mut signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?; + let signalled: Arc> = Arc::new(Mutex::new(false)); let handle = signals.handle(); + let signaler = Arc::clone(&signalled); + tokio::spawn(async move { + // await a signal - once we receive one mark the boolean as true to + // quit the app + signals.next().await; + let mut signalled = signaler.lock().await; + *signalled = true; + }); // TODO: enable isig to re-allow ctrl-z? loop { @@ -74,12 +67,10 @@ impl Tui { } } } - println!("Waiting for signal..."); - if signals.next().await.is_some() { + if *(*signalled).lock().await { break; } } - // TODO main loop handle.close(); stdout().execute(LeaveAlternateScreen)?;