krabka_object_store/
error.rs1use {
4 super::object_store_api as object_store,
5 object_store::path::{Path as ObjectPath, Path},
6 std::io,
7};
8
9#[derive(Debug, thiserror::Error)]
11pub enum ObjectStoreError {
12 #[error("object store I/O error: {0}")]
15 Io(#[from] io::Error),
16 #[error("invalid object store config: {0}")]
19 InvalidConfig(String),
20 #[error("object not found: {0}")]
24 NotFound(ObjectPath),
25 #[error("object `{key}` is {size} bytes, exceeds cap of {max_bytes} bytes")]
28 TooLarge {
29 key: Path,
31 size: u64,
33 max_bytes: u64,
35 },
36 #[error("object already exists: {0}")]
39 AlreadyExists(ObjectPath),
40 #[error("precondition failed for `{key}`: {reason}")]
42 Precondition {
43 key: ObjectPath,
45 reason: String,
47 },
48 #[error("object store backend error: {0}")]
51 Backend(String),
52}
53
54impl From<object_store::Error> for ObjectStoreError {
55 fn from(err: object_store::Error) -> Self {
56 match err {
57 object_store::Error::NotFound { path, .. } => Self::NotFound(ObjectPath::from(path)),
58 object_store::Error::AlreadyExists { path, .. } => {
59 Self::AlreadyExists(ObjectPath::from(path))
60 }
61 object_store::Error::Precondition { path, source } => Self::Precondition {
62 key: ObjectPath::from(path),
63 reason: source.to_string(),
64 },
65 other => Self::Backend(other.to_string()),
66 }
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use {
73 super::{object_store, *},
74 assert2::assert,
75 object_store::path::Path,
76 std::{io, io::ErrorKind},
77 };
78
79 #[test]
80 fn io_error_converts_via_from() {
81 let io = io::Error::new(ErrorKind::NotFound, "nope");
82 let err: ObjectStoreError = io.into();
83 assert!(matches!(err, ObjectStoreError::Io(_)));
84 }
85
86 #[test]
87 fn not_found_maps_to_structured_variant() {
88 let err = object_store::Error::NotFound {
89 path: "tenant/block".to_owned(),
90 source: "missing".into(),
91 };
92 let mapped = ObjectStoreError::from(err);
93 assert!(
94 matches!(&mapped, ObjectStoreError::NotFound(p) if p.to_string() == "tenant/block")
95 );
96 }
97
98 #[test]
99 fn already_exists_maps_to_structured_variant() {
100 let err = object_store::Error::AlreadyExists {
101 path: "worm/manifest.json".to_owned(),
102 source: "exists".into(),
103 };
104 let mapped = ObjectStoreError::from(err);
105 assert!(
106 matches!(&mapped, ObjectStoreError::AlreadyExists(p) if p.to_string() == "worm/manifest.json")
107 );
108 }
109
110 #[test]
111 fn precondition_maps_to_structured_variant() {
112 let err = object_store::Error::Precondition {
113 path: "worm/manifest.json".to_owned(),
114 source: "etag mismatch".into(),
115 };
116 let mapped = ObjectStoreError::from(err);
117 assert!(
118 matches!(&mapped, ObjectStoreError::Precondition { key, reason }
119 if key.to_string() == "worm/manifest.json" && reason == "etag mismatch")
120 );
121 }
122
123 #[test]
124 fn too_large_display_includes_sizes() {
125 let err = ObjectStoreError::TooLarge {
126 key: Path::from("index/snapshot.json"),
127 size: 1000,
128 max_bytes: 256,
129 };
130 let msg = err.to_string();
131 assert!(msg.contains("1000"));
132 assert!(msg.contains("256"));
133 }
134
135 #[test]
136 fn other_errors_map_to_backend() {
137 let err = object_store::Error::Generic {
138 store: "s",
139 source: "boom".into(),
140 };
141 assert!(matches!(
142 ObjectStoreError::from(err),
143 ObjectStoreError::Backend(_)
144 ));
145 }
146}