1use thiserror::Error;
2
3#[derive(Debug, Error)]
4pub enum CsiError {
5 #[error("OCI pull failed: {0}")]
6 PullFailed(Box<cfgd_core::errors::OciError>),
7
8 #[error("invalid volume attribute: {key}")]
9 InvalidAttribute { key: String },
10
11 #[error("IO error: {0}")]
12 Io(#[from] std::io::Error),
13
14 #[error("metrics server error: {0}")]
21 Metrics(String),
22}
23
24impl From<cfgd_core::errors::OciError> for CsiError {
25 fn from(e: cfgd_core::errors::OciError) -> Self {
26 CsiError::PullFailed(Box::new(e))
27 }
28}
29
30#[cfg(test)]
31mod tests {
32 use super::*;
33 use cfgd_core::errors::OciError;
34
35 #[test]
36 fn pullfailed_display_includes_inner_oci_error_message() {
37 let oci_err = OciError::ManifestNotFound {
38 reference: "ghcr.io/example/mod:v1".into(),
39 };
40 let e = CsiError::PullFailed(Box::new(oci_err));
41 assert_eq!(
42 format!("{e}"),
43 "OCI pull failed: manifest not found: ghcr.io/example/mod:v1"
44 );
45 }
46
47 #[test]
48 fn from_oci_error_wraps_in_pullfailed_preserving_inner() {
49 let oci_err = OciError::ManifestNotFound {
50 reference: "ghcr.io/example/mod:v1".into(),
51 };
52 let csi: CsiError = oci_err.into();
53 match csi {
54 CsiError::PullFailed(boxed) => {
55 assert_eq!(
56 format!("{boxed}"),
57 "manifest not found: ghcr.io/example/mod:v1"
58 );
59 }
60 other => panic!("expected PullFailed, got {other:?}"),
61 }
62 }
63
64 #[test]
65 fn invalid_attribute_display_uses_exact_key() {
66 let e = CsiError::InvalidAttribute {
67 key: "csi.cfgd.io/oci-uri".into(),
68 };
69 assert_eq!(
70 format!("{e}"),
71 "invalid volume attribute: csi.cfgd.io/oci-uri"
72 );
73 }
74
75 #[test]
76 fn from_io_error_wraps_in_io_variant_preserving_kind_and_message() {
77 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "no such device");
78 let csi: CsiError = io_err.into();
79 match csi {
80 CsiError::Io(io) => {
81 assert_eq!(io.kind(), std::io::ErrorKind::NotFound);
82 assert_eq!(format!("{io}"), "no such device");
83 }
84 other => panic!("expected Io, got {other:?}"),
85 }
86 }
87
88 #[test]
89 fn io_display_uses_io_error_prefix() {
90 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "no such device");
91 let csi: CsiError = io_err.into();
92 assert_eq!(format!("{csi}"), "IO error: no such device");
93 }
94}