use std::path::PathBuf;
use crate::output::ResolvedConfigFilePath;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Failed to write config file {0}: {1}")]
FailedToWriteConfig(ResolvedConfigFilePath, std::io::Error),
#[error("Failed to read existing config file {0}: {1}")]
FailedToReadConfig(ResolvedConfigFilePath, #[source] std::io::Error),
#[error("Failed to probe filesystem state at {0:?}: {1}")]
SymlinkProbeFailed(PathBuf, #[source] std::io::Error),
#[error("Failed to create symlink {symlink} -> {real}: {source}")]
CreateSymlinkFailed {
real: ResolvedConfigFilePath,
symlink: ResolvedConfigFilePath,
#[source]
source: std::io::Error,
},
#[error("Symlink {symlink} -> {real}: {real} does not exist in the zenops repo")]
SymlinkRealPathMissing {
real: ResolvedConfigFilePath,
symlink: ResolvedConfigFilePath,
},
#[error(
"Not creating symlink at {0}: a non-file, non-directory entry (FIFO, socket, etc.) already exists"
)]
RefusingToOverwriteOtherWithSymlink(ResolvedConfigFilePath),
#[error("Not creating symlink {symlink} -> {real}: a file already exists")]
RefusingToOverwriteFileWithSymlink {
real: ResolvedConfigFilePath,
symlink: ResolvedConfigFilePath,
},
#[error("Not creating symlink {symlink} -> {real}: a directory already exists")]
RefusingToOverwriteDirectoryWithSymlink {
real: ResolvedConfigFilePath,
symlink: ResolvedConfigFilePath,
},
#[error("Failed to create directory {0:?}: {1}")]
CreateDirectoryError(ResolvedConfigFilePath, std::io::Error),
}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::FailedToWriteConfig(l0, l1), Self::FailedToWriteConfig(r0, r1)) => {
l0 == r0 && l1.kind() == r1.kind()
}
(Self::FailedToReadConfig(l0, l1), Self::FailedToReadConfig(r0, r1)) => {
l0 == r0 && l1.kind() == r1.kind()
}
(Self::SymlinkProbeFailed(l0, l1), Self::SymlinkProbeFailed(r0, r1)) => {
l0 == r0 && l1.kind() == r1.kind()
}
(
Self::CreateSymlinkFailed {
real: l_real,
symlink: l_symlink,
source: l_src,
},
Self::CreateSymlinkFailed {
real: r_real,
symlink: r_symlink,
source: r_src,
},
) => l_real == r_real && l_symlink == r_symlink && l_src.kind() == r_src.kind(),
(
Self::SymlinkRealPathMissing {
real: l_real,
symlink: l_symlink,
},
Self::SymlinkRealPathMissing {
real: r_real,
symlink: r_symlink,
},
) => l_real == r_real && l_symlink == r_symlink,
(
Self::RefusingToOverwriteOtherWithSymlink(l0),
Self::RefusingToOverwriteOtherWithSymlink(r0),
) => l0 == r0,
(
Self::RefusingToOverwriteFileWithSymlink {
real: l_real,
symlink: l_symlink,
},
Self::RefusingToOverwriteFileWithSymlink {
real: r_real,
symlink: r_symlink,
},
) => l_real == r_real && l_symlink == r_symlink,
(
Self::RefusingToOverwriteDirectoryWithSymlink {
real: l_real,
symlink: l_symlink,
},
Self::RefusingToOverwriteDirectoryWithSymlink {
real: r_real,
symlink: r_symlink,
},
) => l_real == r_real && l_symlink == r_symlink,
(Self::CreateDirectoryError(l0, l1), Self::CreateDirectoryError(r0, r1)) => {
l0 == r0 && l1.kind() == r1.kind()
}
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use similar_asserts::assert_eq;
use zenops_safe_relative_path::srpath;
use crate::config_files::ConfigFilePath;
use crate::output::ResolvedConfigFilePath;
use super::*;
fn rcfp(rel: &'static str) -> ResolvedConfigFilePath {
let path = ConfigFilePath::Home(Arc::from(
zenops_safe_relative_path::SafeRelativePath::from_relative_path(rel).unwrap(),
));
let full = Arc::from(Path::new("/tmp").join(rel).as_path());
ResolvedConfigFilePath { path, full }
}
fn io(kind: io::ErrorKind) -> io::Error {
io::Error::from(kind)
}
#[test]
fn failed_to_write_config_eq_and_ne() {
let a = Error::FailedToWriteConfig(rcfp("a"), io(io::ErrorKind::PermissionDenied));
let b = Error::FailedToWriteConfig(rcfp("a"), io(io::ErrorKind::PermissionDenied));
let c = Error::FailedToWriteConfig(rcfp("b"), io(io::ErrorKind::PermissionDenied));
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn failed_to_read_config_eq_and_ne() {
let a = Error::FailedToReadConfig(rcfp("a"), io(io::ErrorKind::PermissionDenied));
let b = Error::FailedToReadConfig(rcfp("a"), io(io::ErrorKind::PermissionDenied));
let c = Error::FailedToReadConfig(rcfp("a"), io(io::ErrorKind::NotFound));
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn symlink_probe_failed_eq_and_ne() {
let a = Error::SymlinkProbeFailed(PathBuf::from("/x"), io(io::ErrorKind::Other));
let b = Error::SymlinkProbeFailed(PathBuf::from("/x"), io(io::ErrorKind::Other));
let c = Error::SymlinkProbeFailed(PathBuf::from("/y"), io(io::ErrorKind::Other));
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn create_symlink_failed_eq_and_ne() {
let mk = |real, symlink, kind| Error::CreateSymlinkFailed {
real: rcfp(real),
symlink: rcfp(symlink),
source: io(kind),
};
assert_eq!(
mk("r", "s", io::ErrorKind::AlreadyExists),
mk("r", "s", io::ErrorKind::AlreadyExists)
);
assert_ne!(
mk("r", "s", io::ErrorKind::AlreadyExists),
mk("other", "s", io::ErrorKind::AlreadyExists)
);
assert_ne!(
mk("r", "s", io::ErrorKind::AlreadyExists),
mk("r", "s", io::ErrorKind::NotFound)
);
}
#[test]
fn symlink_real_path_missing_eq_and_ne() {
let a = Error::SymlinkRealPathMissing {
real: rcfp("r"),
symlink: rcfp("s"),
};
let b = Error::SymlinkRealPathMissing {
real: rcfp("r"),
symlink: rcfp("s"),
};
let c = Error::SymlinkRealPathMissing {
real: rcfp("r"),
symlink: rcfp("other"),
};
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn refusing_to_overwrite_other_eq_and_ne() {
let a = Error::RefusingToOverwriteOtherWithSymlink(rcfp("a"));
let b = Error::RefusingToOverwriteOtherWithSymlink(rcfp("a"));
let c = Error::RefusingToOverwriteOtherWithSymlink(rcfp("b"));
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn refusing_to_overwrite_file_eq_and_ne() {
let a = Error::RefusingToOverwriteFileWithSymlink {
real: rcfp("r"),
symlink: rcfp("s"),
};
let b = Error::RefusingToOverwriteFileWithSymlink {
real: rcfp("r"),
symlink: rcfp("s"),
};
let c = Error::RefusingToOverwriteFileWithSymlink {
real: rcfp("r"),
symlink: rcfp("t"),
};
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn refusing_to_overwrite_directory_eq_and_ne() {
let a = Error::RefusingToOverwriteDirectoryWithSymlink {
real: rcfp("r"),
symlink: rcfp("s"),
};
let b = Error::RefusingToOverwriteDirectoryWithSymlink {
real: rcfp("r"),
symlink: rcfp("s"),
};
let c = Error::RefusingToOverwriteDirectoryWithSymlink {
real: rcfp("other"),
symlink: rcfp("s"),
};
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn create_directory_error_eq_and_ne() {
let a = Error::CreateDirectoryError(rcfp("a"), io(io::ErrorKind::PermissionDenied));
let b = Error::CreateDirectoryError(rcfp("a"), io(io::ErrorKind::PermissionDenied));
let c = Error::CreateDirectoryError(rcfp("a"), io(io::ErrorKind::NotFound));
assert_eq!(a, b);
assert_ne!(a, c);
let _ = srpath!("dummy");
}
}