Skip to main content

devboy_assets/
error.rs

1//! Error type for the asset management crate.
2//!
3//! Wraps [`devboy_core::Error`] with a few asset-specific variants. The
4//! [`From`] impls let call sites use `?` with IO, serde_json, and the core
5//! error type interchangeably.
6
7use thiserror::Error;
8
9/// Errors that can occur during asset cache operations.
10#[derive(Error, Debug)]
11pub enum AssetError {
12    /// Underlying filesystem I/O failure.
13    #[error("I/O error: {0}")]
14    Io(#[from] std::io::Error),
15
16    /// Failed to (de)serialize JSON while reading / writing the index.
17    #[error("Index serialization error: {0}")]
18    Serialization(#[from] serde_json::Error),
19
20    /// Failed to parse a human-readable size / duration in configuration.
21    #[error("Configuration error: {0}")]
22    Config(String),
23
24    /// The requested asset was not present in the cache index.
25    #[error("Asset not found: {0}")]
26    NotFound(String),
27
28    /// The cache directory was unreachable or could not be created.
29    #[error("Cache directory error: {0}")]
30    CacheDir(String),
31
32    /// The in-memory index mutex was poisoned by a panic in another
33    /// thread. Callers can decide whether to recover (rebuild index) or
34    /// bubble the error up to the user.
35    #[error("Asset index mutex poisoned: {0}")]
36    Poisoned(String),
37
38    /// Anything surfaced from [`devboy_core::Error`].
39    #[error(transparent)]
40    Core(#[from] devboy_core::Error),
41}
42
43/// Convenience alias for `Result<T, AssetError>`.
44pub type Result<T> = std::result::Result<T, AssetError>;
45
46impl AssetError {
47    /// Create a new configuration error.
48    pub fn config(msg: impl Into<String>) -> Self {
49        AssetError::Config(msg.into())
50    }
51
52    /// Create a new cache directory error.
53    pub fn cache_dir(msg: impl Into<String>) -> Self {
54        AssetError::CacheDir(msg.into())
55    }
56
57    /// Create a new not-found error.
58    pub fn not_found(id: impl Into<String>) -> Self {
59        AssetError::NotFound(id.into())
60    }
61
62    /// Create a new poisoned-mutex error.
63    pub fn poisoned(msg: impl Into<String>) -> Self {
64        AssetError::Poisoned(msg.into())
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn constructor_helpers() {
74        let e = AssetError::config("bad value");
75        assert!(matches!(e, AssetError::Config(_)));
76        assert!(format!("{e}").contains("bad value"));
77
78        let e = AssetError::cache_dir("no perms");
79        assert!(matches!(e, AssetError::CacheDir(_)));
80
81        let e = AssetError::not_found("asset-1");
82        assert!(matches!(e, AssetError::NotFound(_)));
83    }
84
85    #[test]
86    fn io_error_is_convertible() {
87        let io = std::io::Error::new(std::io::ErrorKind::NotFound, "x");
88        let e: AssetError = io.into();
89        assert!(matches!(e, AssetError::Io(_)));
90    }
91}