diff --git a/src/data.rs b/src/data.rs deleted file mode 100644 index a029434..0000000 --- a/src/data.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::{ - fmt::Display, - fs::File, - io::{Read as _, Write as _}, - path::PathBuf, - sync::LazyLock, -}; - -use serde::{Deserialize, Serialize}; - -use crate::cli::THEME; - -pub static DATA_FOLDER: LazyLock = LazyLock::new(|| { - let mut dir = dirs::data_dir().expect(""); - dir.push("git-identity"); - dir -}); - -pub static IDENTITIES_FOLDER: LazyLock = LazyLock::new(|| { - let mut dir = DATA_FOLDER.clone(); - dir.push("identities"); - dir -}); - -#[derive(Debug)] -pub struct Identity { - pub alias: String, - pub data: IdentityData, -} - -#[derive(thiserror::Error, Debug)] -pub enum IdentityOpenError { - #[error("IO error: {0}")] - IO(#[from] std::io::Error), - - #[error("error while deserializing: {0}")] - Deserialize(#[from] toml::de::Error), -} - -impl Identity { - pub fn open(alias: &str) -> Result - where - A: Into, - { - let alias = String::from(alias); - - let path = { - let mut path = DATA_FOLDER.clone(); - path.push("identities"); - path.push(&alias); - path - }; - - let mut file = File::options() - .read(true) - .write(false) - .create(false) - .open(path)?; - - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - - let data = IdentityData::deserialize(toml::Deserializer::parse(&contents)?)?; - - Ok(Self { alias, data }) - } - - pub fn get_path(&self) -> PathBuf { - let mut path = IDENTITIES_FOLDER.clone(); - path.push(&self.alias); - path - } - - pub fn save(&self) -> std::io::Result<()> { - let contents = toml::to_string(&self.data) - .expect("there should only be serializable values in the internal data structure"); - - let path = self.get_path(); - - if let Some(parent) = path.parent() { - std::fs::create_dir_all(parent)?; - } - - File::options() - .write(true) - .truncate(true) - .append(false) - .create(true) - .open(&path)? - .write_all(contents.as_bytes())?; - - Ok(()) - } - - pub fn save_interactively(&self) -> anyhow::Result<()> { - let path = self.get_path(); - - if std::fs::exists(&path)? - && !dialoguer::Confirm::with_theme(&*THEME) - .with_prompt(format!("{} already exists. Ovewrite?", path.display())) - .interact()? - { - Ok(()) - } else { - Ok(self.save()?) - } - } -} - -impl Display for Identity { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "\"{}\":", self.alias)?; - - if let Some(ref name) = self.data.name { - write!(f, " {name}")?; - } - - if let Some(ref email) = self.data.email { - write!(f, " <{email}>")?; - } - - if let Some(ref sigkey) = self.data.sigkey { - write!(f, " (sigkey: {sigkey})")?; - } - - if let Some(ref authkey) = self.data.authkey { - write!(f, " (authkey: {})", authkey.display())?; - } - - Ok(()) - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct IdentityData { - pub name: Option, - pub email: Option, - pub sigkey: Option, - pub authkey: Option, -} diff --git a/src/lib.rs b/src/lib.rs index ed4d21a..c7c54a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,14 @@ //! This crate is not a library but we need some things to be exported so that they can be read by //! cargo xtasks +use std::{path::PathBuf, sync::LazyLock}; + pub mod cli; -pub mod data; pub mod logging; pub mod subcommands; + +pub static DATA_FOLDER: LazyLock = LazyLock::new(|| { + let mut dir = dirs::data_dir().expect(""); + dir.push("git-identity"); + dir +}); diff --git a/src/logging.rs b/src/logging.rs index 0b00787..27e4bed 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -4,7 +4,7 @@ use tracing_subscriber::{Layer as _, layer::SubscriberExt as _, util::Subscriber pub const DEFAULT_LOG_LEVEL: Level = if cfg!(debug_assertions) { Level::DEBUG // debug builds } else { - Level::INFO // release builds + Level::WARN // release builds }; fn level_to_index(level: Level) -> u8 { diff --git a/src/subcommands/import.rs b/src/subcommands/import.rs index fbbd7c8..68470ae 100644 --- a/src/subcommands/import.rs +++ b/src/subcommands/import.rs @@ -25,27 +25,17 @@ pub fn main(_global_cli: crate::cli::GlobalArgs, cli: crate::cli::ImportCli) -> .to_str() .expect("this should always be valid UTF-8"); - let alias = dialoguer::Input::::with_theme(&*THEME) + let identity_name = dialoguer::Input::::with_theme(&*THEME) .with_prompt("What should this identity be named?") .interact_text()?; - let authkey: Option = select_authkey(&mut ctx, &key, &alias)?; + let authkey: Option = select_authkey(&mut ctx, &key, &identity_name)?; let (name, email) = select_name_and_email(&key)?; - let identity = crate::data::Identity { - alias, - data: crate::data::IdentityData { - name, - email, - sigkey: Some(fingerprint.to_owned()), - authkey, - }, - }; - - identity.save_interactively()?; - - info!("Imported identity {identity}"); + info!( + "Imported identity: [{identity_name}] {name:?} <{email:?}> (signing key: {fingerprint}, authentication key: {authkey:?})" + ); Ok(()) } @@ -217,17 +207,6 @@ fn select_authkey( ) -> anyhow::Result> { let mut authkey_prompt = dialoguer::Input::::with_theme(&*THEME) .with_prompt("Select an SSH key (leave empty for none)") - .validate_with(|input: &String| -> Result<(), Cow> { - if input.is_empty() { - return Ok(()); - } - - match std::fs::exists(input) { - Ok(true) => Ok(()), - Ok(false) => Err(Cow::Borrowed("file not found")), - Err(err) => Err(Cow::Owned(err.to_string())), - } - }) .allow_empty(true); if let Some(authkey) = try_export_as_ssh_key(ctx, key, identity_name)? { @@ -269,7 +248,7 @@ fn try_export_as_ssh_key( } let default_destination = { - let mut path = crate::data::DATA_FOLDER.clone(); + let mut path = crate::DATA_FOLDER.clone(); path.push("ssh"); path.push(format!("{identity_name}.pub")); path