diff --git a/src/git.rs b/src/git.rs index bce9dbb..02175b7 100644 --- a/src/git.rs +++ b/src/git.rs @@ -23,7 +23,7 @@ use std::collections::HashMap; use std::fs::canonicalize; use std::os::unix::fs::symlink; use std::path::Path; -use std::{fs, process::Command}; +use std::{fmt, fs, process::Command}; use crate::settings; use crate::utils::strings::{failure_str, success_str}; @@ -123,54 +123,91 @@ pub struct SeriesItem<'series> { pub closure: Box (bool)>, } -fn handle_file_exists(selff: &Link, tx_path: &Path, rx_path: &Path) -> bool { +#[derive(Debug)] +pub enum LinkError { + AlreadyLinked(String, String), + DifferentLink(String, String), + FileExists(String, String), + BrokenSymlinkExists(String, String), + FailedCreatingLink(String, String), + IoError(std::io::Error), +} + +impl std::fmt::Display for LinkError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LinkError::AlreadyLinked(tx, rx) => { + write!(f, "Linking {tx} -> {rx} failed: file already linked") + } + LinkError::DifferentLink(tx, rx) => write!( + f, + "Linking {tx} -> {rx} failed: link to different file exists" + ), + LinkError::FileExists(tx, rx) => write!(f, "Linking {tx} -> {rx} failed: file exists"), + LinkError::BrokenSymlinkExists(tx, rx) => { + write!(f, "Linking {tx} -> {rx} failed: broken symlink") + } + LinkError::FailedCreatingLink(tx, rx) => write!(f, "Linking {tx} -> {rx} failed"), + LinkError::IoError(err) => write!(f, "IO Error: {err}"), + } + } +} + +impl std::error::Error for LinkError { + // TODO: I'm too tired to implement soucce... yawn, eepy + // fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + // match self { + // LinkError::AlreadyLinked(tx, rx) => Some(rx), + // LinkError::DifferentLink(tx, rx) => Some(rx), + // LinkError::FileExists(tx, rx) => Some(rx), + // } + // } +} + +impl From for LinkError { + fn from(err: std::io::Error) -> LinkError { + LinkError::IoError(err) + } +} + +fn handle_file_exists(selff: &Link, tx_path: &Path, rx_path: &Path) -> Result { match rx_path.read_link() { - Ok(file) - if file.canonicalize().expect("failed to canonicalize file") - == tx_path.canonicalize().expect("failed to canonicalize path") => - { - debug!( - "Linking {} -> {} failed: file already linked", - &selff.tx, &selff.rx - ); - false - } - Ok(file) => { - error!( - "Linking {} -> {} failed: link to different file exists", - &selff.tx, &selff.rx - ); - false - } - Err(error) => { - error!("Linking {} -> {} failed: file exists", &selff.tx, &selff.rx); - false + Ok(file) if file.canonicalize()? == tx_path.canonicalize()? => { + Err(LinkError::AlreadyLinked( + tx_path.to_string_lossy().to_string(), + rx_path.to_string_lossy().to_string(), + )) } + Ok(file) => Err(LinkError::DifferentLink( + tx_path.to_string_lossy().to_string(), + rx_path.to_string_lossy().to_string(), + )), + Err(error) => Err(LinkError::FileExists( + tx_path.to_string_lossy().to_string(), + rx_path.to_string_lossy().to_string(), + )), } } impl Link { /// Creates the link from the link struct - pub fn link(&self) -> bool { + pub fn link(&self) -> Result { let tx_path: &Path = std::path::Path::new(&self.tx); let rx_path: &Path = std::path::Path::new(&self.rx); match rx_path.try_exists() { Ok(true) => handle_file_exists(self, tx_path, rx_path), - Ok(false) if rx_path.is_symlink() => { - error!( - "Linking {} -> {} failed: broken symlink", - &self.tx, &self.rx - ); - false - } + Ok(false) if rx_path.is_symlink() => Err(LinkError::FileExists( + tx_path.to_string_lossy().to_string(), + rx_path.to_string_lossy().to_string(), + )), Ok(false) => { - symlink(&self.tx, &self.rx).expect("failed to create link"); - true - } - Err(error) => { - error!("Linking {} -> {} failed: {}", &self.tx, &self.rx, error); - false + symlink(&self.tx, &self.rx)?; + Ok(true) } + Err(error) => Err(LinkError::FailedCreatingLink( + tx_path.to_string_lossy().to_string(), + rx_path.to_string_lossy().to_string(), + )), } } } @@ -551,7 +588,7 @@ impl Config { /// Runs associated function on all links in config fn on_all_links_spinner(&self, op: &str, f: F) where - F: Fn(&Link) -> bool, + F: Fn(&Link) -> Result, { for category in self.categories.values() { match category.links.as_ref() { @@ -560,16 +597,30 @@ impl Config { if !settings::QUIET.load(std::sync::atomic::Ordering::Relaxed) { let mut sp = Spinner::new(Spinners::Dots10, format!("{}: {}", link.name, op)); - if f(link) { - sp.stop_and_persist( - success_str(), - format!("{}: {}", link.name, op), - ); - } else { - sp.stop_and_persist( + match f(link) { + Err(e @ LinkError::AlreadyLinked(_, _)) => { + sp.stop_and_persist(success_str(), format!("{e}")) + } + Err(e @ LinkError::DifferentLink(_, _)) => { + sp.stop_and_persist(failure_str(), format!("{e}")) + } + Err(e @ LinkError::FileExists(_, _)) => { + sp.stop_and_persist(failure_str(), format!("{e}")) + } + Err(e @ LinkError::BrokenSymlinkExists(_, _)) => { + sp.stop_and_persist(failure_str(), format!("{e}")) + } + Err(e @ LinkError::FailedCreatingLink(_, _)) => { + sp.stop_and_persist(failure_str(), format!("{e}")) + } + Err(e @ LinkError::IoError(_)) => { + sp.stop_and_persist(failure_str(), format!("{e}")) + } + Err(e) => sp.stop_and_persist(failure_str(), format!("{e}")), + _ => sp.stop_and_persist( failure_str(), format!("{}: {}", link.name, op), - ); + ), } } else { f(link);