From b46ddb2d6a3041a64b7bea22a0e4ea2fa5f856a5 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Thu, 12 Sep 2024 20:14:44 +0200 Subject: [PATCH] feat: add config Signed-off-by: kjuulh --- Cargo.lock | 87 ++++++++++++++++ crates/gitnow/Cargo.toml | 2 + crates/gitnow/src/main.rs | 197 +++++++++++++++++++++++++++++++++++- examples/config/gitnow.toml | 7 ++ 4 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 examples/config/gitnow.toml diff --git a/Cargo.lock b/Cargo.lock index bb82a47..137047f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,17 @@ version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -163,6 +174,12 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "getrandom" version = "0.2.15" @@ -185,15 +202,23 @@ name = "gitnow" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "clap", "dotenv", "serde", "tokio", + "toml", "tracing", "tracing-subscriber", "uuid", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "heck" version = "0.5.0" @@ -206,6 +231,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -386,6 +421,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -476,6 +520,40 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tracing" version = "0.1.40" @@ -661,3 +739,12 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] diff --git a/crates/gitnow/Cargo.toml b/crates/gitnow/Cargo.toml index 2ab2460..a3dfebf 100644 --- a/crates/gitnow/Cargo.toml +++ b/crates/gitnow/Cargo.toml @@ -14,3 +14,5 @@ dotenv.workspace = true serde = { version = "1.0.197", features = ["derive"] } uuid = { version = "1.7.0", features = ["v4"] } +async-trait = "0.1.82" +toml = "0.8.19" diff --git a/crates/gitnow/src/main.rs b/crates/gitnow/src/main.rs index 54961c7..3a403c9 100644 --- a/crates/gitnow/src/main.rs +++ b/crates/gitnow/src/main.rs @@ -1,5 +1,6 @@ use anyhow::Context; use clap::{Parser, Subcommand}; +use commands::root::RootCommand; #[derive(Parser)] #[command(author, version, about, long_about = Some("Navigate git projects at the speed of thought"))] @@ -17,14 +18,208 @@ enum Commands { async fn main() -> anyhow::Result<()> { dotenv::dotenv().ok(); tracing_subscriber::fmt::init(); + let app = app::App::new_static(); let cli = Command::parse(); tracing::debug!("Starting cli"); match cli.command { Some(_) => todo!(), - None => todo!(), + None => { + RootCommand::new(app).execute().await?; + } } Ok(()) } + +mod config { + use std::path::Path; + + use anyhow::Context; + use serde::{Deserialize, Serialize}; + + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct Config { + #[serde(default)] + pub providers: Providers, + } + + #[derive(Debug, Default, Serialize, Deserialize, PartialEq)] + pub struct Providers { + #[serde(default)] + pub github: Vec, + #[serde(default)] + pub gitea: Vec, + } + + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct GitHub { + #[serde(default)] + pub users: Vec, + #[serde(default)] + pub organisations: Vec, + } + + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct GitHubUser(String); + + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct GitHubOrganisation(String); + + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct Gitea { + #[serde(default)] + pub users: Vec, + #[serde(default)] + pub organisations: Vec, + } + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct GiteaUser(String); + + #[derive(Debug, Serialize, Deserialize, PartialEq)] + pub struct GiteaOrganisation(String); + + impl Config { + pub async fn from_file(file_path: &Path) -> anyhow::Result { + let file_content = tokio::fs::read_to_string(file_path).await?; + + Self::from_string(&file_content) + } + + pub fn from_string(content: &str) -> anyhow::Result { + toml::from_str(content).context("failed to deserialize config file") + } + } + + #[cfg(test)] + mod test { + use super::*; + + #[test] + fn test_can_parse_config() -> anyhow::Result<()> { + let content = r#" + [[providers.github]] + users = ["kjuulh"] + organisations = ["lunarway"] + + [[providers.github]] + users = ["other"] + organisations = ["org"] + + [[providers.gitea]] + users = ["kjuulh"] + organisations = ["lunarway"] + + [[providers.gitea]] + users = ["other"] + organisations = ["org"] + + [[providers.gitea]] + "#; + + let config = Config::from_string(content)?; + + assert_eq!( + Config { + providers: Providers { + github: vec![ + GitHub { + users: vec![GitHubUser("kjuulh".into())], + organisations: vec![GitHubOrganisation("lunarway".into())] + }, + GitHub { + users: vec![GitHubUser("other".into())], + organisations: vec![GitHubOrganisation("org".into())] + } + ], + gitea: vec![ + Gitea { + users: vec![GiteaUser("kjuulh".into())], + organisations: vec![GiteaOrganisation("lunarway".into())] + }, + Gitea { + users: vec![GiteaUser("other".into())], + organisations: vec![GiteaOrganisation("org".into())] + }, + Gitea { + users: vec![], + organisations: vec![] + }, + ] + } + }, + config + ); + + Ok(()) + } + + #[test] + fn test_can_parse_empty_config() -> anyhow::Result<()> { + let content = r#" + # empty file + "#; + + let config = Config::from_string(content)?; + + assert_eq!( + Config { + providers: Providers { + github: vec![], + gitea: vec![] + } + }, + config + ); + + Ok(()) + } + } +} + +mod git_provider { + use async_trait::async_trait; + + pub struct Repository {} + + #[async_trait] + pub trait GitProvider { + async fn list_repositories(&self) -> anyhow::Result>; + } +} + +mod app { + #[derive(Debug)] + pub struct App {} + + impl App { + pub fn new_static() -> &'static App { + Box::leak(Box::new(App {})) + } + } +} + +mod commands { + pub mod root { + use crate::app::App; + + #[derive(Debug, Clone)] + pub struct RootCommand { + app: &'static App, + } + + impl RootCommand { + pub fn new(app: &'static App) -> Self { + Self { app } + } + + #[tracing::instrument] + pub async fn execute(&mut self) -> anyhow::Result<()> { + tracing::debug!("executing"); + + Ok(()) + } + } + } +} diff --git a/examples/config/gitnow.toml b/examples/config/gitnow.toml new file mode 100644 index 0000000..45f34e7 --- /dev/null +++ b/examples/config/gitnow.toml @@ -0,0 +1,7 @@ +[[providers.github]] +users = ["kjuulh"] +organisations = ["lunarway"] + +[[providers.gitea]] +users = ["kjuulh"] +organisation = ["noorgplease"]