use std::io;
use thiserror::Error;
use uuid::Uuid;
#[derive(Debug, Error)]
pub enum CitadelError {
#[error("State not found: {0}")]
StateNotFound(Uuid),
#[error("Corrupted state file: {0}")]
CorruptedState(String),
#[error("Incompatible state version: expected {expected}, found {found}")]
IncompatibleVersion {
expected: String,
found: String,
},
#[error("I/O error: {0}")]
IoError(#[from] io::Error),
#[error("Serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("Invalid state directory: {0}")]
InvalidDirectory(String),
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("Failed to create state directory: {0}")]
DirectoryCreationFailed(String),
}
impl CitadelError {
pub fn corrupted(message: impl Into<String>) -> Self {
Self::CorruptedState(message.into())
}
pub fn incompatible(expected: impl Into<String>, found: impl Into<String>) -> Self {
Self::IncompatibleVersion {
expected: expected.into(),
found: found.into(),
}
}
pub fn invalid_directory(message: impl Into<String>) -> Self {
Self::InvalidDirectory(message.into())
}
pub fn permission_denied(message: impl Into<String>) -> Self {
Self::PermissionDenied(message.into())
}
pub fn directory_creation_failed(message: impl Into<String>) -> Self {
Self::DirectoryCreationFailed(message.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_state_not_found_error() {
let id = Uuid::new_v4();
let error = CitadelError::StateNotFound(id);
let error_msg = error.to_string();
assert!(error_msg.contains("State not found"));
assert!(error_msg.contains(&id.to_string()));
}
#[test]
fn test_corrupted_state_error() {
let error = CitadelError::corrupted("Invalid JSON structure");
let error_msg = error.to_string();
assert!(error_msg.contains("Corrupted state file"));
assert!(error_msg.contains("Invalid JSON structure"));
}
#[test]
fn test_incompatible_version_error() {
let error = CitadelError::incompatible("1.0.0", "2.0.0");
let error_msg = error.to_string();
assert!(error_msg.contains("Incompatible state version"));
assert!(error_msg.contains("expected 1.0.0"));
assert!(error_msg.contains("found 2.0.0"));
}
#[test]
fn test_io_error_conversion() {
let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
let citadel_err: CitadelError = io_err.into();
assert!(matches!(citadel_err, CitadelError::IoError(_)));
}
#[test]
fn test_serialization_error_conversion() {
let json = "{invalid json}";
let serde_err = serde_json::from_str::<serde_json::Value>(json).unwrap_err();
let citadel_err: CitadelError = serde_err.into();
assert!(matches!(citadel_err, CitadelError::SerializationError(_)));
}
#[test]
fn test_invalid_directory_error() {
let error = CitadelError::invalid_directory("/invalid/path");
assert!(error.to_string().contains("Invalid state directory"));
assert!(error.to_string().contains("/invalid/path"));
}
#[test]
fn test_permission_denied_error() {
let error = CitadelError::permission_denied("Cannot write to directory");
assert!(error.to_string().contains("Permission denied"));
}
#[test]
fn test_directory_creation_failed_error() {
let error = CitadelError::directory_creation_failed("Path already exists as file");
assert!(
error
.to_string()
.contains("Failed to create state directory")
);
}
}