Skip to main content

pacha/
error.rs

1//! Error types for Pacha registry operations.
2
3use std::path::PathBuf;
4use thiserror::Error;
5
6/// Result type alias for Pacha operations.
7pub type Result<T> = std::result::Result<T, PachaError>;
8
9/// Errors that can occur during Pacha registry operations.
10#[derive(Error, Debug)]
11pub enum PachaError {
12    /// Database operation failed.
13    #[error("database error: {0}")]
14    Database(#[from] rusqlite::Error),
15
16    /// IO operation failed.
17    #[error("IO error: {0}")]
18    Io(#[from] std::io::Error),
19
20    /// JSON serialization/deserialization failed.
21    #[error("JSON error: {0}")]
22    Json(#[from] serde_json::Error),
23
24    /// TOML serialization/deserialization failed.
25    #[error("TOML error: {0}")]
26    TomlDeserialize(#[from] toml::de::Error),
27
28    /// TOML serialization failed.
29    #[error("TOML serialization error: {0}")]
30    TomlSerialize(#[from] toml::ser::Error),
31
32    /// `MessagePack` serialization/deserialization failed.
33    #[error("MessagePack error: {0}")]
34    MessagePack(String),
35
36    /// Artifact not found.
37    #[error("artifact not found: {kind} '{name}' version {version}")]
38    NotFound {
39        /// Kind of artifact (model, dataset, recipe).
40        kind: String,
41        /// Name of the artifact.
42        name: String,
43        /// Version requested.
44        version: String,
45    },
46
47    /// Artifact already exists.
48    #[error("artifact already exists: {kind} '{name}' version {version}")]
49    AlreadyExists {
50        /// Kind of artifact.
51        kind: String,
52        /// Name of the artifact.
53        name: String,
54        /// Version that exists.
55        version: String,
56    },
57
58    /// Invalid version string.
59    #[error("invalid version string: {0}")]
60    InvalidVersion(String),
61
62    /// Content hash mismatch.
63    #[error("content hash mismatch: expected {expected}, got {actual}")]
64    HashMismatch {
65        /// Expected hash.
66        expected: String,
67        /// Actual hash computed.
68        actual: String,
69    },
70
71    /// Storage path error.
72    #[error("storage path error: {0}")]
73    StoragePath(PathBuf),
74
75    /// Compression error.
76    #[error("compression error: {0}")]
77    Compression(String),
78
79    /// Invalid artifact state transition.
80    #[error("invalid stage transition from {from} to {to}")]
81    InvalidStageTransition {
82        /// Current stage.
83        from: String,
84        /// Attempted target stage.
85        to: String,
86    },
87
88    /// Validation error.
89    #[error("validation error: {0}")]
90    Validation(String),
91
92    /// Registry not initialized.
93    #[error("registry not initialized at {0}")]
94    NotInitialized(PathBuf),
95
96    /// Invalid URI format.
97    #[error("invalid URI: {0}")]
98    InvalidUri(String),
99
100    /// Operation not supported.
101    #[error("unsupported operation '{operation}': {reason}")]
102    UnsupportedOperation {
103        /// Operation that was attempted.
104        operation: String,
105        /// Reason it's not supported.
106        reason: String,
107    },
108
109    /// Signature verification failed.
110    #[error("signature verification failed")]
111    SignatureInvalid,
112
113    /// Remote registry error.
114    #[error("remote registry error: {0}")]
115    RemoteRegistry(String),
116
117    /// Invalid format (encryption, file structure).
118    #[error("invalid format: {0}")]
119    InvalidFormat(String),
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_error_display_not_found() {
128        let err = PachaError::NotFound {
129            kind: "model".to_string(),
130            name: "fraud-detector".to_string(),
131            version: "1.0.0".to_string(),
132        };
133        assert_eq!(err.to_string(), "artifact not found: model 'fraud-detector' version 1.0.0");
134    }
135
136    #[test]
137    fn test_error_display_hash_mismatch() {
138        let err = PachaError::HashMismatch {
139            expected: "abc123".to_string(),
140            actual: "def456".to_string(),
141        };
142        assert_eq!(err.to_string(), "content hash mismatch: expected abc123, got def456");
143    }
144
145    #[test]
146    fn test_error_display_invalid_stage() {
147        let err = PachaError::InvalidStageTransition {
148            from: "development".to_string(),
149            to: "archived".to_string(),
150        };
151        assert_eq!(err.to_string(), "invalid stage transition from development to archived");
152    }
153}