storage_kit/core/error.rs
1use thiserror::Error;
2
3/// Portable error type returned by the storage Kit.
4///
5/// Portable request families report unsupported backend behavior explicitly
6/// instead of synthesizing successful responses. Use [`StorageError::InvalidRequest`]
7/// for semantically invalid caller input that is already structurally valid Rust.
8///
9/// # Examples
10///
11/// ```
12/// let error = storage_kit::StorageError::InvalidRequest(
13/// "vector value cannot be coerced without precision loss".into(),
14/// );
15///
16/// assert!(error.to_string().contains("invalid request"));
17/// ```
18///
19#[derive(Debug, Clone, Error)]
20pub enum StorageError {
21 /// The supplied configuration is structurally invalid.
22 #[error("invalid configuration: {0}")]
23 InvalidConfig(String),
24
25 /// A portable request is structurally valid Rust but semantically invalid.
26 ///
27 /// Vector coercion policy failures and malformed typed requests use this
28 /// variant before a backend operation is attempted.
29 #[error("invalid request: {0}")]
30 InvalidRequest(String),
31
32 /// The selected backend does not expose the requested operation yet.
33 #[error("database operation is not implemented yet: {note}")]
34 NotYetImplemented {
35 /// Human-readable note describing the missing support.
36 note: String,
37 },
38
39 /// The backend or mode does not support a requested capability.
40 #[error("unsupported capability `{capability}`{context}")]
41 UnsupportedCapability {
42 /// Missing or unsupported capability name.
43 capability: String,
44 /// Additional human-readable context.
45 context: String,
46 },
47
48 /// A backend-specific operation failed.
49 #[error("database operation `{operation}` failed: {message}")]
50 BackendOperation {
51 /// Portable operation label used by the adapter.
52 operation: String,
53 /// Human-readable backend error message.
54 message: String,
55 },
56
57 /// Serialization or wire-format conversion failed.
58 #[error("serialization error: {0}")]
59 Serialization(String),
60
61 /// Filesystem or OS I/O failed.
62 #[error("io error: {0}")]
63 Io(String),
64}
65
66impl StorageError {
67 /// Creates a [`StorageError::NotYetImplemented`] value.
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// let error = storage_kit::StorageError::not_yet_implemented(
73 /// "retained operation mapping is pending",
74 /// );
75 /// assert!(matches!(
76 /// error,
77 /// storage_kit::StorageError::NotYetImplemented { .. }
78 /// ));
79 /// ```
80 pub fn not_yet_implemented(note: impl Into<String>) -> Self {
81 Self::NotYetImplemented { note: note.into() }
82 }
83
84 /// Creates a [`StorageError::UnsupportedCapability`] value.
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// let error = storage_kit::StorageError::unsupported_capability(
90 /// "FULL_TEXT",
91 /// "selected backend cannot provide this full-text query",
92 /// );
93 /// assert!(error.to_string().contains("FULL_TEXT"));
94 /// ```
95 pub fn unsupported_capability(
96 capability: impl Into<String>,
97 context: impl Into<String>,
98 ) -> Self {
99 let context = context.into();
100 Self::UnsupportedCapability {
101 capability: capability.into(),
102 context: if context.is_empty() {
103 String::new()
104 } else {
105 format!(" ({context})")
106 },
107 }
108 }
109
110 /// Creates a [`StorageError::BackendOperation`] value.
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// let error = storage_kit::StorageError::backend_op(
116 /// "query",
117 /// "provider returned an error",
118 /// );
119 /// let cloned = error.clone();
120 /// assert_eq!(error.to_string(), cloned.to_string());
121 /// ```
122 pub fn backend_op(operation: impl Into<String>, message: impl Into<String>) -> Self {
123 Self::BackendOperation {
124 operation: operation.into(),
125 message: message.into(),
126 }
127 }
128}
129
130impl From<serde_json::Error> for StorageError {
131 fn from(value: serde_json::Error) -> Self {
132 Self::Serialization(value.to_string())
133 }
134}
135
136impl From<std::io::Error> for StorageError {
137 fn from(value: std::io::Error) -> Self {
138 Self::Io(value.to_string())
139 }
140}