refactor(link): add LinkError and propagate errors up to runner #14

Merged
cafkafk merged 6 commits from cafk-link-result-refactor into main 2023-10-17 07:09:30 +02:00

View file

@ -23,7 +23,7 @@ use std::collections::HashMap;
use std::fs::canonicalize; use std::fs::canonicalize;
use std::os::unix::fs::symlink; use std::os::unix::fs::symlink;
use std::path::Path; use std::path::Path;
use std::{fs, process::Command}; use std::{fmt, fs, process::Command};
use crate::settings; use crate::settings;
use crate::utils::strings::{failure_str, success_str}; use crate::utils::strings::{failure_str, success_str};
@ -123,54 +123,91 @@ pub struct SeriesItem<'series> {
pub closure: Box<dyn Fn(&Repo) -> (bool)>, pub closure: Box<dyn Fn(&Repo) -> (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<std::io::Error> 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<bool, LinkError> {
match rx_path.read_link() { match rx_path.read_link() {
Ok(file) Ok(file) if file.canonicalize()? == tx_path.canonicalize()? => {
if file.canonicalize().expect("failed to canonicalize file") Err(LinkError::AlreadyLinked(
== tx_path.canonicalize().expect("failed to canonicalize path") => tx_path.to_string_lossy().to_string(),
{ rx_path.to_string_lossy().to_string(),
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) => 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 { impl Link {
/// Creates the link from the link struct /// Creates the link from the link struct
pub fn link(&self) -> bool { pub fn link(&self) -> Result<bool, LinkError> {
let tx_path: &Path = std::path::Path::new(&self.tx); let tx_path: &Path = std::path::Path::new(&self.tx);
let rx_path: &Path = std::path::Path::new(&self.rx); let rx_path: &Path = std::path::Path::new(&self.rx);
match rx_path.try_exists() { match rx_path.try_exists() {
Ok(true) => handle_file_exists(self, tx_path, rx_path), Ok(true) => handle_file_exists(self, tx_path, rx_path),
Ok(false) if rx_path.is_symlink() => { Ok(false) if rx_path.is_symlink() => Err(LinkError::FileExists(
error!( tx_path.to_string_lossy().to_string(),
"Linking {} -> {} failed: broken symlink", rx_path.to_string_lossy().to_string(),
&self.tx, &self.rx )),
);
false
}
Ok(false) => { Ok(false) => {
symlink(&self.tx, &self.rx).expect("failed to create link"); symlink(&self.tx, &self.rx)?;
true Ok(true)
}
Err(error) => {
error!("Linking {} -> {} failed: {}", &self.tx, &self.rx, error);
false
} }
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 /// Runs associated function on all links in config
fn on_all_links_spinner<F>(&self, op: &str, f: F) fn on_all_links_spinner<F>(&self, op: &str, f: F)
where where
F: Fn(&Link) -> bool, F: Fn(&Link) -> Result<bool, LinkError>,
{ {
for category in self.categories.values() { for category in self.categories.values() {
match category.links.as_ref() { match category.links.as_ref() {
@ -560,16 +597,30 @@ impl Config {
if !settings::QUIET.load(std::sync::atomic::Ordering::Relaxed) { if !settings::QUIET.load(std::sync::atomic::Ordering::Relaxed) {
let mut sp = let mut sp =
Spinner::new(Spinners::Dots10, format!("{}: {}", link.name, op)); Spinner::new(Spinners::Dots10, format!("{}: {}", link.name, op));
if f(link) { match f(link) {
sp.stop_and_persist( Err(e @ LinkError::AlreadyLinked(_, _)) => {
success_str(), sp.stop_and_persist(success_str(), format!("{e}"))
format!("{}: {}", link.name, op), }
); Err(e @ LinkError::DifferentLink(_, _)) => {
} else { sp.stop_and_persist(failure_str(), format!("{e}"))
sp.stop_and_persist( }
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(), failure_str(),
format!("{}: {}", link.name, op), format!("{}: {}", link.name, op),
); ),
} }
} else { } else {
f(link); f(link);