diff --git a/Cargo.lock b/Cargo.lock index 8a3d876..6f36dbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -114,6 +125,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55d05712b2d8d88102bc9868020c9e5c7a1f5527c452b9b97450a1d006140ba7" +dependencies = [ + "serde", +] + [[package]] name = "errno" version = "0.3.8" @@ -164,10 +184,15 @@ dependencies = [ name = "fuk" version = "0.1.0" dependencies = [ + "async-trait", "futures", "reqwest", + "serde", + "serde_json", "tokio", "tokio-stream", + "toml", + "typetag", ] [[package]] @@ -387,6 +412,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + [[package]] name = "ipnet" version = "2.9.0" @@ -730,6 +761,15 @@ dependencies = [ "serde", ] +[[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" @@ -899,6 +939,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[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" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -930,6 +1004,30 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typetag" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43148481c7b66502c48f35b8eef38b6ccdc7a9f04bd4cc294226d901ccc9bc7" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291db8a81af4840c10d636e047cac67664e343be44e24dfdbd1492df9a5d3390" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -1191,6 +1289,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index 6b2357f..3c48fb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-trait = "0.1.77" futures = "0.3.30" reqwest = "0.11.24" +serde = { version = "1.0.196", features = ["derive"] } +serde_json = "1.0.113" tokio = { version = "1.36.0", default-features = false, features = ["rt-multi-thread", "fs", "io-std", "io-util", "macros", "process", "net"] } tokio-stream = "0.1.14" +toml = "0.8.10" +typetag = "0.2.15" diff --git a/flake.nix b/flake.nix index 35ab22b..6ce979c 100644 --- a/flake.nix +++ b/flake.nix @@ -24,9 +24,23 @@ systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ]; - perSystem = { inputs', ... }: { + perSystem = { inputs', pkgs, ... }: { devenv.shells.default = { languages.rust.enable = true; + + packages = + [ + pkgs.scdoc + pkgs.cargo-nextest + pkgs.cargo-outdated + ] + ++ pkgs.lib.lists.optionals pkgs.stdenv.isDarwin (with pkgs.darwin.apple_sdk; [ + frameworks.Foundation + frameworks.CoreFoundation + frameworks.SystemConfiguration + frameworks.Security + pkgs.libiconv + ]); }; }; }; diff --git a/src/config.rs b/src/config.rs index 5deba75..728a783 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,49 +1,39 @@ use crate::output::Output; +use crate::sources::*; + use futures::prelude::*; -struct Entry { - name: String, - keys: Vec, +#[derive(Debug, serde::Deserialize)] +pub struct Entry { + pub name: String, + pub keys: Vec>, } impl Entry { - fn fetch(&self) -> (String, impl Stream + '_) { - let stream = - stream::iter(&self.keys) - .flat_map(Source::fetch); + pub async fn fetch(&self) -> (String, Vec) { + let stream = stream::iter(&self.keys) + .then(|k| async { k.fetch().await }) + .map(stream::iter) + .flatten() + .collect() + .await; (self.name.clone(), stream) } } -enum Source { - Raw(String), - Sourcehut { user: String }, - Github { user: String, instance: String }, - Host(Vec), -} - -impl Source { - fn fetch(&self) -> impl Stream { - match *self { - Source::Raw(ref key) => stream::iter(vec![key.clone()]), - Source::Host(ref _hosts) => stream::iter(vec![]), - _ => unimplemented!(), - } - } -} - +#[derive(Debug, serde::Deserialize)] pub struct Config { - users: Vec, + #[serde(rename = "entry")] + pub entries: Vec, } impl Config { pub async fn fetch(&self) -> Result { - let keys = - stream::iter(&self.users) - .map(Entry::fetch) + let keys = stream::iter(&self.entries) + .then(Entry::fetch) .collect() - .await; + .await; Ok(Output { keys }) } diff --git a/src/lib.rs b/src/lib.rs index 90354f0..e63d471 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ -pub mod output; pub mod config; +pub mod output; +pub mod sources; -pub fn fuk() { - println!("Fuk"); +pub async fn fuk(config: config::Config) -> output::Output { + config.fetch().await.unwrap() } diff --git a/src/main.rs b/src/main.rs index b7f0146..24c4ab1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,24 @@ +const EXAMPLE: &'static str = r#" +[[entry]] +name = "hauleth" +keys = [ + { Forge = { user = "~hauleth", host = "meta.sr.ht" } } +] + +[[entry]] +name = "heimdall" +keys = [ + { Host = [ "heimdall" ] } +] +"#; + #[tokio::main] async fn main() { - fuk::fuk(); + let config: fuk::config::Config = + toml::from_str(EXAMPLE).unwrap(); + + println!("{:#?}", config); + + let output = fuk::fuk(config).await; + println!("{}", serde_json::to_string_pretty(&output.keys).unwrap()); } diff --git a/src/output/mod.rs b/src/output/mod.rs index 2cbe057..844ec2e 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +#[derive(Debug, serde::Serialize)] pub struct Output { pub keys: HashMap>, } diff --git a/src/sources/mod.rs b/src/sources/mod.rs new file mode 100644 index 0000000..7e7ddde --- /dev/null +++ b/src/sources/mod.rs @@ -0,0 +1,76 @@ +use tokio::process::Command; + +use serde::Deserialize; +use async_trait::async_trait; + +#[typetag::deserialize] +#[async_trait] +pub trait Fetch: std::fmt::Debug { + async fn fetch(&self) -> Vec; +} + +#[derive(Debug, Deserialize)] +pub struct Raw(Vec); + +#[typetag::deserialize] +#[async_trait::async_trait] +impl Fetch for Raw { + async fn fetch(&self) -> Vec { + self.0.clone() + } +} + +#[derive(Debug, Deserialize)] +pub struct Host(pub Vec); + +#[typetag::deserialize] +#[async_trait] +impl Fetch for Host { + async fn fetch(&self) -> Vec { + let result = Command::new("ssh-keyscan") + .args(&self.0) + .output() + .await + .unwrap(); + + std::str::from_utf8(&result.stdout) + .unwrap() + .trim() + .split('\n') + .map(|line| { + // Ignore first column as it contain hostname which is not + // needed there + line.split(' ') + .skip(1) + .collect::>() + .join(" ") + .to_owned() + }) + .collect() + } +} + +#[derive(Debug, Deserialize)] +pub struct Forge { + pub user: String, + pub host: String, +} + +#[typetag::deserialize] +#[async_trait] +impl Fetch for Forge { + async fn fetch(&self) -> Vec { + let url = format!("https://{}/{}.keys", self.host, self.user); + + reqwest::get(url) + .await + .unwrap() + .text() + .await + .unwrap() + .trim() + .split('\n') + .map(|s| s.to_owned()) + .collect() + } +}