treewide: migrate to multi-crate layout
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I11a2103f3530f07409177404577b90136a6a6964
This commit is contained in:
parent
f655b133d4
commit
d445b1814a
68 changed files with 247 additions and 72 deletions
292
crates/pakker-core/src/error.rs
Normal file
292
crates/pakker-core/src/error.rs
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, PakkerError>;
|
||||
|
||||
/// Severity level for errors
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum ErrorSeverity {
|
||||
/// Fatal error - operation cannot continue
|
||||
#[default]
|
||||
Error,
|
||||
|
||||
/// Warning - operation can continue but may have issues
|
||||
Warning,
|
||||
|
||||
/// Info - informational message
|
||||
Info,
|
||||
}
|
||||
|
||||
/// Container for multiple errors that occurred during an operation
|
||||
#[derive(Debug)]
|
||||
pub struct MultiError {
|
||||
errors: Vec<PakkerError>,
|
||||
}
|
||||
|
||||
impl MultiError {
|
||||
pub const fn new() -> Self {
|
||||
Self { errors: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, error: PakkerError) {
|
||||
self.errors.push(error);
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, errors: impl IntoIterator<Item = PakkerError>) {
|
||||
self.errors.extend(errors);
|
||||
}
|
||||
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.errors.is_empty()
|
||||
}
|
||||
|
||||
pub const fn len(&self) -> usize {
|
||||
self.errors.len()
|
||||
}
|
||||
|
||||
pub fn into_result<T>(self, success_value: T) -> Result<T> {
|
||||
if self.is_empty() {
|
||||
Ok(success_value)
|
||||
} else {
|
||||
Err(PakkerError::Multiple(self.errors))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn errors(&self) -> &[PakkerError] {
|
||||
&self.errors
|
||||
}
|
||||
|
||||
pub fn into_errors(self) -> Vec<PakkerError> {
|
||||
self.errors
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MultiError {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<PakkerError> for MultiError {
|
||||
fn from_iter<I: IntoIterator<Item = PakkerError>>(iter: I) -> Self {
|
||||
Self {
|
||||
errors: iter.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PakkerError {
|
||||
// Network errors
|
||||
#[error("Network request failed: {0}")]
|
||||
NetworkError(#[from] reqwest::Error),
|
||||
|
||||
#[error("Platform API error: {0}")]
|
||||
PlatformApiError(String),
|
||||
|
||||
// Validation errors
|
||||
#[error("Invalid lock file: {0}")]
|
||||
InvalidLockFile(String),
|
||||
|
||||
#[error("Invalid config file: {0}")]
|
||||
InvalidConfigFile(String),
|
||||
|
||||
#[error("Project not found: {0}")]
|
||||
ProjectNotFound(String),
|
||||
|
||||
#[error("File selection error: {0}")]
|
||||
FileSelectionError(String),
|
||||
|
||||
#[error("File not found: {0}")]
|
||||
FileNotFound(String),
|
||||
|
||||
// Conflict errors
|
||||
#[error("Circular dependency detected: {0}")]
|
||||
CircularDependency(String),
|
||||
|
||||
// File I/O errors
|
||||
#[error("IO error: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("Serialization error: {0}")]
|
||||
SerializationError(#[from] serde_json::Error),
|
||||
|
||||
#[error("Hash mismatch for file {file}: expected {expected}, got {actual}")]
|
||||
HashMismatch {
|
||||
file: String,
|
||||
expected: String,
|
||||
actual: String,
|
||||
},
|
||||
|
||||
#[error("Download failed: {0}")]
|
||||
DownloadFailed(String),
|
||||
|
||||
// Export errors
|
||||
#[error("Export failed: {0}")]
|
||||
ExportFailed(String),
|
||||
|
||||
#[error("Invalid export profile: {0}")]
|
||||
InvalidExportProfile(String),
|
||||
|
||||
// General errors
|
||||
#[error("Configuration error: {0}")]
|
||||
ConfigError(String),
|
||||
|
||||
#[error("Internal error: {0}")]
|
||||
InternalError(String),
|
||||
|
||||
#[error("Already exists: {0}")]
|
||||
AlreadyExists(String),
|
||||
|
||||
#[error("Invalid input: {0}")]
|
||||
InvalidInput(String),
|
||||
|
||||
#[error("Invalid project: {0}")]
|
||||
InvalidProject(String),
|
||||
|
||||
#[error("Invalid import file: {0}")]
|
||||
InvalidImportFile(String),
|
||||
|
||||
#[error("Zip error: {0}")]
|
||||
ZipError(#[from] zip::result::ZipError),
|
||||
|
||||
// Git and Fork errors
|
||||
#[error("Git error: {0}")]
|
||||
GitError(String),
|
||||
|
||||
#[error("Remote not found: {0}")]
|
||||
RemoteNotFound(String),
|
||||
|
||||
#[error("Fork error: {0}")]
|
||||
Fork(String),
|
||||
|
||||
#[error("Invalid hash: {0}")]
|
||||
InvalidHash(String),
|
||||
|
||||
#[error("Invalid response: {0}")]
|
||||
InvalidResponse(String),
|
||||
|
||||
#[error("IPC error: {0}")]
|
||||
IpcError(String),
|
||||
|
||||
#[error("{}", format_multiple_errors(.0))]
|
||||
Multiple(Vec<Self>),
|
||||
}
|
||||
|
||||
fn format_multiple_errors(errors: &[PakkerError]) -> String {
|
||||
if errors.len() == 1 {
|
||||
return errors[0].to_string();
|
||||
}
|
||||
|
||||
let mut msg = format!("{} errors occurred:\n", errors.len());
|
||||
for (idx, error) in errors.iter().enumerate() {
|
||||
let _ = writeln!(msg, " {}. {}", idx + 1, error);
|
||||
}
|
||||
msg
|
||||
}
|
||||
|
||||
impl From<git2::Error> for PakkerError {
|
||||
fn from(err: git2::Error) -> Self {
|
||||
Self::GitError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::ipc::IpcError> for PakkerError {
|
||||
fn from(err: crate::ipc::IpcError) -> Self {
|
||||
Self::IpcError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_multi_error_empty() {
|
||||
let multi = MultiError::new();
|
||||
assert!(multi.is_empty());
|
||||
assert_eq!(multi.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_error_push() {
|
||||
let mut multi = MultiError::new();
|
||||
multi.push(PakkerError::ProjectNotFound("mod1".to_string()));
|
||||
multi.push(PakkerError::ProjectNotFound("mod2".to_string()));
|
||||
|
||||
assert!(!multi.is_empty());
|
||||
assert_eq!(multi.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_error_into_result_empty() {
|
||||
let multi = MultiError::new();
|
||||
let result: Result<i32> = multi.into_result(42);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_error_into_result_with_errors() {
|
||||
let mut multi = MultiError::new();
|
||||
multi.push(PakkerError::ProjectNotFound("mod1".to_string()));
|
||||
|
||||
let result: Result<i32> = multi.into_result(42);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_error_from_iterator() {
|
||||
let errors = vec![
|
||||
PakkerError::ProjectNotFound("mod1".to_string()),
|
||||
PakkerError::ProjectNotFound("mod2".to_string()),
|
||||
];
|
||||
let multi: MultiError = errors.into_iter().collect();
|
||||
assert_eq!(multi.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_error_extend() {
|
||||
let mut multi = MultiError::new();
|
||||
multi.push(PakkerError::ProjectNotFound("mod1".to_string()));
|
||||
|
||||
let more_errors = vec![
|
||||
PakkerError::ProjectNotFound("mod2".to_string()),
|
||||
PakkerError::ProjectNotFound("mod3".to_string()),
|
||||
];
|
||||
multi.extend(more_errors);
|
||||
|
||||
assert_eq!(multi.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_errors_formatting() {
|
||||
let errors = vec![
|
||||
PakkerError::ProjectNotFound("mod1".to_string()),
|
||||
PakkerError::ProjectNotFound("mod2".to_string()),
|
||||
];
|
||||
let error = PakkerError::Multiple(errors);
|
||||
let msg = error.to_string();
|
||||
|
||||
assert!(msg.contains("2 errors occurred"));
|
||||
assert!(msg.contains("mod1"));
|
||||
assert!(msg.contains("mod2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_multiple_error_formatting() {
|
||||
let errors = vec![PakkerError::ProjectNotFound("mod1".to_string())];
|
||||
let error = PakkerError::Multiple(errors);
|
||||
let msg = error.to_string();
|
||||
|
||||
// Single error should just display the error itself
|
||||
assert!(msg.contains("mod1"));
|
||||
assert!(!msg.contains("errors occurred"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_severity_default() {
|
||||
assert_eq!(ErrorSeverity::default(), ErrorSeverity::Error);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue