From 78315f80a4134c20eba8bd551c8f0191053ed90f Mon Sep 17 00:00:00 2001 From: core Date: Thu, 27 Nov 2025 20:24:36 -0500 Subject: [PATCH] chore: please thy lord clippy --- src/cmds/createid.rs | 31 +++++++++++++++------- src/cmds/import.rs | 36 ++++++++++++------------- src/cmds/ls.rs | 29 ++++++++++++++------ src/cmds/mod.rs | 2 +- src/cmds/show.rs | 24 ++++++++++++----- src/db.rs | 52 ++++++++++++++++++++++++------------ src/identity.rs | 16 +++++------ src/main.rs | 34 +++++++++--------------- src/wrapper.rs | 63 ++++++++++++++++++++++++++------------------ 9 files changed, 172 insertions(+), 115 deletions(-) diff --git a/src/cmds/createid.rs b/src/cmds/createid.rs index 468236abeb240dd9a1670dc879cc0722bed862bd..b31e8747c36f4548f6bfc039db719bc5ae34c5a6 100644 --- a/src/cmds/createid.rs +++ b/src/cmds/createid.rs @@ -1,9 +1,9 @@ -use std::path::Path; -use std::process::exit; -use age::secrecy::ExposeSecret; -use regex::Regex; use crate::db::Database; use crate::identity::{IdKeyData, Identity}; +use age::secrecy::ExposeSecret; +use regex::Regex; +use std::path::Path; +use std::process::exit; pub fn create_id(db_path: &Path, name: String, email: String) -> anyhow::Result<()> { let mut db = Database::load_or_create(db_path)?; @@ -11,7 +11,11 @@ pub fn create_id(db_path: &Path, name: String, email: String) -> anyhow::Result< // sanity check to ensure this id does not already exist for key in &db.keys { if key.name == name && key.email == email { - eprintln!("key '{:?}' (keyid {}) already exists in database", key, key.keys.keyid()?); + eprintln!( + "key '{:?}' (keyid {}) already exists in database", + key, + key.keys.keyid()? + ); eprintln!("if you want to recreate it, remove it first"); exit(1); } @@ -19,11 +23,14 @@ pub fn create_id(db_path: &Path, name: String, email: String) -> anyhow::Result< // Email is a fucking nightmare // RFC5322, I hate you - let email_re = Regex::new(r#"(?x) + let email_re = Regex::new( + r#"(?x) ^(?P[^@\s]+)@ ([[:word:]]+\.)* [[:word:]]+$ - "#).unwrap(); + "#, + ) + .unwrap(); if !email_re.is_match(&email) { eprintln!("`{email}` is not a valid RFC5322 email address"); @@ -37,14 +44,18 @@ pub fn create_id(db_path: &Path, name: String, email: String) -> anyhow::Result< let identity = Identity { name, email, - keys: IdKeyData::Local(ss.to_string()) + keys: IdKeyData::Local(ss.to_string()), }; db.keys.push(identity.clone()); // save database db.write(db_path)?; - println!("wrote new key {:?} (keyid {}) to database", identity, identity.keys.keyid()?); + println!( + "wrote new key {:?} (keyid {}) to database", + identity, + identity.keys.keyid()? + ); Ok(()) -} \ No newline at end of file +} diff --git a/src/cmds/import.rs b/src/cmds/import.rs index 28381a0f78ad6a03f71858e2f0d93db23e480a64..b9b1f7fdba0b7b3fa818d75df2c15b6eed6620f6 100644 --- a/src/cmds/import.rs +++ b/src/cmds/import.rs @@ -1,8 +1,7 @@ -use std::convert::identity; +use crate::db::Database; +use crate::identity::{IdKeyData, Identity, unescape}; use std::path::Path; use std::process::exit; -use crate::db::Database; -use crate::identity::{unescape, IdKeyData, Identity}; pub fn import_key(db_path: &Path, key: String, insert_anyway: bool) -> anyhow::Result<()> { // decode the key @@ -13,35 +12,32 @@ pub fn import_key(db_path: &Path, key: String, insert_anyway: bool) -> anyhow::R exit(1); } - let [_sentinel, key, name, email] = components.as_slice() else { unreachable!() }; + let [_sentinel, key, name, email] = components.as_slice() else { + unreachable!() + }; let name = unescape(name.to_string())?; let email = unescape(email.to_string())?; let keys = match &key[0..3] { - "age" => { - IdKeyData::Peer(key.to_string()) - }, - "AGE" => { - IdKeyData::Local(key.to_string()) - }, + "age" => IdKeyData::Peer(key.to_string()), + "AGE" => IdKeyData::Local(key.to_string()), _ => { eprintln!("unsupported age key type; is this a sage key?"); exit(1); } }; - let identity = Identity { - name, - email, - keys - }; + let identity = Identity { name, email, keys }; let mut db = Database::load_or_create(db_path)?; for key in &db.keys { if key.keys.keyid()? == identity.keys.keyid()? { - println!("key {} already exists in database, skipping", key.keys.keyid()?); + println!( + "key {} already exists in database, skipping", + key.keys.keyid()? + ); return Ok(()); } @@ -56,7 +52,11 @@ pub fn import_key(db_path: &Path, key: String, insert_anyway: bool) -> anyhow::R db.write(db_path)?; - println!("imported {:?} (keyid {}) successfully", identity, identity.keys.keyid()?); + println!( + "imported {:?} (keyid {}) successfully", + identity, + identity.keys.keyid()? + ); Ok(()) -} \ No newline at end of file +} diff --git a/src/cmds/ls.rs b/src/cmds/ls.rs index 286455f6908b8d36aafb296ccb5c0424b224b67f..0428e11aa195442828190d19810684551956d63d 100644 --- a/src/cmds/ls.rs +++ b/src/cmds/ls.rs @@ -1,10 +1,14 @@ +use crate::db::Database; +use crate::identity::{IdKeyData, escape}; use std::path::Path; use std::process::exit; -use urlencoding::encode; -use crate::db::Database; -use crate::identity::{escape, IdKeyData}; -pub fn list_keys(db_path: &Path, local_only: bool, peer_only: bool, full_pks: bool) -> anyhow::Result<()> { +pub fn list_keys( + db_path: &Path, + local_only: bool, + peer_only: bool, + full_pks: bool, +) -> anyhow::Result<()> { if local_only && peer_only { eprintln!("cannot show only local and only peer keys, pick one"); exit(1); @@ -19,19 +23,28 @@ pub fn list_keys(db_path: &Path, local_only: bool, peer_only: bool, full_pks: bo for key in &db.keys { match key.keys { IdKeyData::Local(_) => { - if !show_local { continue }; + if !show_local { + continue; + }; displayed += 1; println!("local: {:?}", key); } IdKeyData::Peer(_) => { - if !show_peer { continue }; + if !show_peer { + continue; + }; displayed += 1; println!("peer: {:?}", key); } } if full_pks { - println!(" pk: sage/{}/{}/{}", key.keys.pk()?, escape(key.name.clone()), escape(key.email.clone())); + println!( + " pk: sage/{}/{}/{}", + key.keys.pk()?, + escape(key.name.clone()), + escape(key.email.clone()) + ); } else { println!(" id: {}", key.keys.keyid()?); } @@ -42,4 +55,4 @@ pub fn list_keys(db_path: &Path, local_only: bool, peer_only: bool, full_pks: bo } Ok(()) -} \ No newline at end of file +} diff --git a/src/cmds/mod.rs b/src/cmds/mod.rs index 94142315069d58b5d663b17d5b6261329931299c..3521ee481415f235c2154f36bcc6c7dd24c5e291 100644 --- a/src/cmds/mod.rs +++ b/src/cmds/mod.rs @@ -1,4 +1,4 @@ pub mod createid; +pub mod import; pub mod ls; pub mod show; -pub mod import; \ No newline at end of file diff --git a/src/cmds/show.rs b/src/cmds/show.rs index b8e1ad594c0e88534c348add92048157f9dad04e..2f0ab10d4ce047c462d77a4f77fee1e6efec3ea7 100644 --- a/src/cmds/show.rs +++ b/src/cmds/show.rs @@ -1,7 +1,7 @@ +use crate::db::Database; +use crate::identity::{IdKeyData, escape}; use std::path::Path; use std::process::exit; -use crate::db::Database; -use crate::identity::{escape, IdKeyData}; pub fn show_key(db_path: &Path, search: String, expose_secret: bool) -> anyhow::Result<()> { let db = Database::load_or_create(db_path)?; @@ -21,12 +21,24 @@ pub fn show_key(db_path: &Path, search: String, expose_secret: bool) -> anyhow:: let show_sk = expose_secret && matches!(each_match.keys, IdKeyData::Local(..)); if !show_sk { - println!(" pk: sage/{}/{}/{}", each_match.keys.pk()?, escape(each_match.name.clone()), escape(each_match.email.clone())); + println!( + " pk: sage/{}/{}/{}", + each_match.keys.pk()?, + escape(each_match.name.clone()), + escape(each_match.email.clone()) + ); } else { - let IdKeyData::Local(k) = &each_match.keys else { continue }; - println!(" sk: sage/{}/{}/{}", k, escape(each_match.name.clone()), escape(each_match.email.clone())); + let IdKeyData::Local(k) = &each_match.keys else { + continue; + }; + println!( + " sk: sage/{}/{}/{}", + k, + escape(each_match.name.clone()), + escape(each_match.email.clone()) + ); } } Ok(()) -} \ No newline at end of file +} diff --git a/src/db.rs b/src/db.rs index cf8201caadbb830388e28eab4f5e4878306cbf3c..633c1136d39fbce54896cf85b27e8b1861f59b7b 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,19 +1,17 @@ -use std::path::{Path, PathBuf}; +use crate::identity::Identity; use dirs::config_dir; use nucleo::{Config, Matcher, Utf32Str}; use serde::{Deserialize, Serialize}; -use crate::identity::Identity; +use std::path::{Path, PathBuf}; #[derive(Serialize, Deserialize)] pub struct Database { - pub keys: Vec + pub keys: Vec, } impl Database { pub fn load_or_create(path: &Path) -> anyhow::Result { if !database_exists(path) { - let d = Database { - keys: vec![] - }; + let d = Database { keys: vec![] }; write_database(d, path)?; } load_database(path) @@ -22,19 +20,39 @@ impl Database { write_database(self, path) } pub fn fuzzy_search(&self, term: String) -> Vec { - if self.keys.is_empty() { return vec![] } + if self.keys.is_empty() { + return vec![]; + } let mut m = Matcher::new(Config::DEFAULT); - let haystacks = self.keys.iter().map(|u| { - (u.clone(), format!("{:?} {}", u, u.keys.pk().unwrap_or("".to_string()))) - }).collect::>(); + let haystacks = self + .keys + .iter() + .map(|u| { + ( + u.clone(), + format!( + "{:?} {}", + u, + u.keys.pk().unwrap_or("".to_string()) + ), + ) + }) + .collect::>(); - let mut scores = haystacks.iter().map(|h| { - ( - m.fuzzy_match(Utf32Str::Ascii(h.1.as_bytes()), Utf32Str::Ascii(term.as_bytes())).unwrap_or(0), - h.0.clone() - ) - }).collect::>(); + let mut scores = haystacks + .iter() + .map(|h| { + ( + m.fuzzy_match( + Utf32Str::Ascii(h.1.as_bytes()), + Utf32Str::Ascii(term.as_bytes()), + ) + .unwrap_or(0), + h.0.clone(), + ) + }) + .collect::>(); scores.sort_by_key(|u| u.0); scores.reverse(); @@ -71,4 +89,4 @@ fn write_database(d: Database, path: &Path) -> anyhow::Result<()> { pub fn db_default() -> PathBuf { config_dir().unwrap().join("sage.toml") -} \ No newline at end of file +} diff --git a/src/identity.rs b/src/identity.rs index ee63d40c10bc7b7c55761cb2e4aaa9e71936b1bb..1872230d1a649193043742bf09619229571ec6b4 100644 --- a/src/identity.rs +++ b/src/identity.rs @@ -1,18 +1,18 @@ -use std::fmt::{Debug, Formatter}; -use std::str::FromStr; use anyhow::bail; use serde::{Deserialize, Serialize}; +use std::fmt::{Debug, Formatter}; +use std::str::FromStr; #[derive(Serialize, Deserialize, Clone)] pub struct Identity { pub name: String, pub email: String, - pub keys: IdKeyData + pub keys: IdKeyData, } #[derive(Serialize, Deserialize, Clone)] pub enum IdKeyData { Local(String), - Peer(String) + Peer(String), } impl Debug for Identity { @@ -31,16 +31,14 @@ impl IdKeyData { } }; Ok(k.to_public().to_string()) - }, - IdKeyData::Peer(pk) => { - Ok(pk.clone()) } + IdKeyData::Peer(pk) => Ok(pk.clone()), } } pub fn keyid(&self) -> anyhow::Result { let pk = self.pk()?; let s0 = &pk[4..12]; - Ok(format!("{s0}")) + Ok(s0.to_string()) } } @@ -49,4 +47,4 @@ pub fn escape(i: String) -> String { } pub fn unescape(i: String) -> anyhow::Result { Ok(urlencoding::decode(&i)?.to_string()) -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index c1c7cbc5188895ac5ec6f0e941de871bd41bf6e7..3f16aec838edb2d998f6e8bc2b43a55e7ef55fcb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,11 @@ -use std::path::PathBuf; -use std::process::exit; -use dirs::config_dir; use crate::cmds::createid::create_id; use crate::db::db_default; use crate::wrapper::wrapper; +use std::process::exit; -mod identity; -mod db; mod cmds; +mod db; +mod identity; mod wrapper; fn main() -> anyhow::Result<()> { @@ -18,7 +16,9 @@ fn main() -> anyhow::Result<()> { exit(0); } - let database = pargs.opt_value_from_str(["-D", "--database"])?.unwrap_or(db_default()); + let database = pargs + .opt_value_from_str(["-D", "--database"])? + .unwrap_or(db_default()); let Some(subcommand) = pargs.subcommand()? else { // run the wrapper @@ -37,7 +37,7 @@ fn main() -> anyhow::Result<()> { }; create_id(&database, name, email)?; - }, + } "ls" => { cmds::ls::list_keys( &database, @@ -45,29 +45,21 @@ fn main() -> anyhow::Result<()> { pargs.contains(["-p", "--peer"]), pargs.contains(["-f", "--full"]), )?; - }, + } "show" => { let Ok(search) = pargs.free_from_str() else { eprintln!("search term is required, --help for help"); exit(1); }; - cmds::show::show_key( - &database, - search, - pargs.contains(["-e", "--expose-secret"]) - )?; - }, + cmds::show::show_key(&database, search, pargs.contains(["-e", "--expose-secret"]))?; + } "import" => { let Ok(key) = pargs.free_from_str() else { eprintln!("key is required, --help for help"); exit(1); }; - cmds::import::import_key( - &database, - key, - pargs.contains(["-i", "--import-anyway"]) - )?; - }, + cmds::import::import_key(&database, key, pargs.contains(["-i", "--import-anyway"]))?; + } unknown => { eprintln!("Unknown subcommand: {}", unknown); print_help(); @@ -158,4 +150,4 @@ import [OPTIONS] OPTIONS: -i, --insert-anyway Insert even if a matching identity is already present -"; \ No newline at end of file +"; diff --git a/src/wrapper.rs b/src/wrapper.rs index 9aa400098da2fcec447eff56ace2f10e36a9827a..d48a40c222de6fee36b5d776e704b1fa91ccfdf7 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -1,19 +1,17 @@ +use crate::db::Database; +use crate::identity::IdKeyData; +use anyhow::bail; +use pico_args::Arguments; use std::os::unix::prelude::CommandExt; use std::path::{Path, PathBuf}; -use std::process::{exit, Command, Stdio}; +use std::process::{Command, exit}; use std::str::FromStr; -use std::thread::sleep; -use std::time::Duration; -use anyhow::bail; -use pico_args::Arguments; use tempfile::NamedTempFile; -use crate::db::Database; -use crate::identity::IdKeyData; #[derive(Debug)] enum Mode { Encrypt, - Decrypt + Decrypt, } pub fn wrapper(mut pargs: Arguments, db_path: &Path) -> anyhow::Result<()> { @@ -25,7 +23,11 @@ pub fn wrapper(mut pargs: Arguments, db_path: &Path) -> anyhow::Result<()> { bail!("cannot specify both --encrypt and --decrypt, pick one"); } - let mode = if contains_decrypt { Mode::Decrypt } else { Mode::Encrypt }; + let mode = if contains_decrypt { + Mode::Decrypt + } else { + Mode::Encrypt + }; let output: Option = pargs.opt_value_from_str(["-o", "--output"])?; let armor = pargs.contains(["-a", "--armor"]); let passphrase = pargs.contains(["-p", "--passphrase"]); @@ -37,11 +39,12 @@ pub fn wrapper(mut pargs: Arguments, db_path: &Path) -> anyhow::Result<()> { if let Some(r) = maybe_r { recipients.push(r); } else { - break + break; } } - if let Ok(Some(f)) = pargs.opt_value_from_str::<[&str; 2], PathBuf>(["-R", "--recipients-file"]) { + if let Ok(Some(f)) = pargs.opt_value_from_str::<[&str; 2], PathBuf>(["-R", "--recipients-file"]) + { recipients.extend(load_keyfile(&f)?); } @@ -50,32 +53,37 @@ pub fn wrapper(mut pargs: Arguments, db_path: &Path) -> anyhow::Result<()> { let maybe_i: Option = pargs.opt_value_from_str(["-i", "--identity"])?; if let Some(i) = maybe_i { - if let Ok(path) = PathBuf::from_str(&i) && path.exists() { + let Ok(path) = PathBuf::from_str(&i); + if path.exists() { identities.extend(load_keyfile(&path)?); } else { identities.push(i); } } else { - break + break; } } let input: Option = pargs.opt_free_from_str()?; if verbose { - println!("resolved args: {:?} output={:?} armor?{:?} passphrase?{:?} recipients=>{:?} identities=>{:?} input={:?}", mode, output, armor, passphrase, recipients, identities, input); + println!( + "resolved args: {:?} output={:?} armor?{:?} passphrase?{:?} recipients=>{:?} identities=>{:?} input={:?}", + mode, output, armor, passphrase, recipients, identities, input + ); } // Resolve recipients and identities to the database let db = Database::load_or_create(db_path)?; // resolve recipients - let resolved_recipients = recipients.iter() + let resolved_recipients = recipients + .iter() .map(|u| { // is this already a valid key? if u.starts_with("age1") { // regular ol' pubkey - return u.clone() + return u.clone(); } // otherwise, resolve it in the database let results = db.fuzzy_search(u.clone()); @@ -90,7 +98,11 @@ pub fn wrapper(mut pargs: Arguments, db_path: &Path) -> anyhow::Result<()> { let r = &results[0]; if verbose { - eprintln!("resolved rcpt {u} => {:?} (keyid {})", r, r.keys.keyid().unwrap()); + eprintln!( + "resolved rcpt {u} => {:?} (keyid {})", + r, + r.keys.keyid().unwrap() + ); } r.keys.pk().unwrap() @@ -142,10 +154,13 @@ pub fn wrapper(mut pargs: Arguments, db_path: &Path) -> anyhow::Result<()> { // execute age let mut args = vec![]; - args.push(match mode { - Mode::Encrypt => "--encrypt", - Mode::Decrypt => "--decrypt" - }.to_string()); + args.push( + match mode { + Mode::Encrypt => "--encrypt", + Mode::Decrypt => "--decrypt", + } + .to_string(), + ); if let Some(o) = output { args.push("--output".to_string()); args.push(o.display().to_string()); @@ -172,9 +187,7 @@ pub fn wrapper(mut pargs: Arguments, db_path: &Path) -> anyhow::Result<()> { eprintln!("exec age {:?}", args); } - Err(Command::new("age") - .args(args) - .exec())?; + Err(Command::new("age").args(args).exec())?; Ok(()) } @@ -186,4 +199,4 @@ fn load_keyfile(path: &Path) -> anyhow::Result> { .filter(|u| !u.starts_with("#")) .map(|u| u.to_string()) .collect()) -} \ No newline at end of file +}