use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ValidationError {
#[error("Window name cannot be empty")]
EmptyName,
#[error("Window name must start with a letter or underscore, found '{0}'")]
InvalidFirstChar(char),
#[error(
"Window name contains invalid characters (only letters, numbers, and underscores allowed)"
)]
InvalidCharacters,
#[error("'{0}' is a reserved name")]
ReservedName(String),
}
#[derive(Debug, Error)]
pub enum PathError {
#[error(
"Absolute paths are not allowed: {0}\nhelp: Use a relative path within the project, e.g., 'src/ui/orders/'"
)]
AbsolutePath(PathBuf),
#[error(
"Path '{path}' is outside the project directory\nhelp: Use a relative path within the project, e.g., 'src/ui/orders/'"
)]
OutsideProject {
path: PathBuf,
project_root: PathBuf,
},
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
}
#[derive(Debug, Error)]
pub enum ProjectError {
#[error(
"Not a Dampen project: Cargo.toml not found in current directory or any parent directory\nhelp: Run 'dampen new <project_name>' to create a new Dampen project"
)]
CargoTomlNotFound,
#[error(
"Not a Dampen project: dampen-core dependency not found in Cargo.toml\nhelp: Add dampen-core to [dependencies] or run 'dampen new' to create a new project"
)]
NotDampenProject,
#[error("Failed to read Cargo.toml: {0}")]
IoError(#[from] std::io::Error),
#[error("Failed to parse Cargo.toml: {0}")]
ParseError(#[from] toml::de::Error),
}
#[derive(Debug, Error)]
pub enum GenerationError {
#[error(
"Window '{window_name}' already exists at {path}\nhelp: Choose a different name or remove the existing file first"
)]
FileExists {
window_name: String,
path: PathBuf,
},
#[error("Failed to create directory {path}: {source}")]
DirectoryCreation {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("Failed to write file {path}: {source}")]
FileWrite {
path: PathBuf,
#[source]
source: std::io::Error,
},
}
#[derive(Debug, Error)]
pub enum IntegrationError {
#[error("Failed to read {path}: {source}")]
ModFileRead {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("Failed to write {path}: {source}")]
ModFileWrite {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("Failed to create directory {path}: {source}")]
DirectoryCreation {
path: PathBuf,
#[source]
source: std::io::Error,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validation_error_empty_name() {
let err = ValidationError::EmptyName;
assert_eq!(err.to_string(), "Window name cannot be empty");
}
#[test]
fn test_validation_error_invalid_first_char() {
let err = ValidationError::InvalidFirstChar('9');
assert_eq!(
err.to_string(),
"Window name must start with a letter or underscore, found '9'"
);
}
#[test]
fn test_validation_error_invalid_characters() {
let err = ValidationError::InvalidCharacters;
assert_eq!(
err.to_string(),
"Window name contains invalid characters (only letters, numbers, and underscores allowed)"
);
}
#[test]
fn test_validation_error_reserved_name() {
let err = ValidationError::ReservedName("mod".to_string());
assert_eq!(err.to_string(), "'mod' is a reserved name");
}
#[test]
fn test_path_error_absolute_path() {
let err = PathError::AbsolutePath(PathBuf::from("/absolute/path"));
let msg = err.to_string();
assert!(msg.contains("Absolute paths are not allowed"));
assert!(msg.contains("/absolute/path"));
assert!(msg.contains("help:"));
}
#[test]
fn test_path_error_outside_project() {
let err = PathError::OutsideProject {
path: PathBuf::from("../outside"),
project_root: PathBuf::from("/project"),
};
let msg = err.to_string();
assert!(msg.contains("outside the project directory"));
assert!(msg.contains("help:"));
}
#[test]
fn test_project_error_cargo_toml_not_found() {
let err = ProjectError::CargoTomlNotFound;
let msg = err.to_string();
assert!(msg.contains("Cargo.toml not found"));
assert!(msg.contains("help:"));
assert!(msg.contains("dampen new"));
}
#[test]
fn test_project_error_not_dampen_project() {
let err = ProjectError::NotDampenProject;
let msg = err.to_string();
assert!(msg.contains("dampen-core dependency not found"));
assert!(msg.contains("help:"));
}
#[test]
fn test_generation_error_file_exists() {
let err = GenerationError::FileExists {
window_name: "settings".to_string(),
path: PathBuf::from("src/ui/settings.rs"),
};
let msg = err.to_string();
assert!(msg.contains("already exists"));
assert!(msg.contains("settings"));
assert!(msg.contains("help:"));
}
#[test]
fn test_generation_error_directory_creation() {
let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "permission denied");
let err = GenerationError::DirectoryCreation {
path: PathBuf::from("src/ui/admin"),
source: io_err,
};
let msg = err.to_string();
assert!(msg.contains("Failed to create directory"));
assert!(msg.contains("src/ui/admin"));
}
#[test]
fn test_generation_error_file_write() {
let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "permission denied");
let err = GenerationError::FileWrite {
path: PathBuf::from("src/ui/settings.rs"),
source: io_err,
};
let msg = err.to_string();
assert!(msg.contains("Failed to write file"));
assert!(msg.contains("src/ui/settings.rs"));
}
}