1use std::{borrow::Cow, collections::BTreeMap, fmt, path::PathBuf};
2
3use bstr::BString;
4
5use crate::{Class, ResourceExhaustionKind};
6
7pub type Metadata = BTreeMap<Cow<'static, str>, MetadataValue>;
13
14pub struct Message {
35 pub message: Cow<'static, str>,
37 pub class: Option<Class>,
39 pub values: Metadata,
41}
42
43impl Message {
44 pub fn new(message: impl Into<Cow<'static, str>>) -> Self {
46 Self {
47 message: message.into(),
48 class: None,
49 values: Metadata::new(),
50 }
51 }
52
53 pub fn with_class(mut self, class: Class) -> Self {
55 self.class = Some(class);
56 self
57 }
58
59 pub fn with(mut self, key: impl Into<Cow<'static, str>>, value: impl Into<MetadataValue>) -> Self {
62 self.values.insert(key.into(), value.into());
63 self
64 }
65}
66
67impl fmt::Debug for Message {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 let mut debug = f.debug_struct("Message");
70 debug.field("message", &self.message);
71 if let Some(class) = self.class {
72 debug.field("class", &format_args!("{class:?}"));
73 }
74 if !self.values.is_empty() {
75 debug.field("values", &format_args!("{:?}", self.values));
76 }
77 debug.finish()
78 }
79}
80
81impl fmt::Display for Message {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 f.write_str(&self.message)?;
84 for (key, value) in &self.values {
85 write!(f, ", {key:?}={value}")?;
86 }
87 Ok(())
88 }
89}
90
91impl std::error::Error for Message {}
92
93impl From<Cow<'static, str>> for Message {
94 fn from(message: Cow<'static, str>) -> Self {
95 Self::new(message)
96 }
97}
98
99impl From<String> for Message {
100 fn from(message: String) -> Self {
101 Self::new(message)
102 }
103}
104
105impl From<&'static str> for Message {
106 fn from(message: &'static str) -> Self {
107 Self::new(message)
108 }
109}
110
111pub fn validation(message: impl Into<Cow<'static, str>>) -> Message {
113 Message::new(message).with_class(Class::Validation)
114}
115
116pub fn corruption(message: impl Into<Cow<'static, str>>) -> Message {
118 Message::new(message).with_class(Class::Corruption)
119}
120
121pub fn not_found(message: impl Into<Cow<'static, str>>) -> Message {
123 Message::new(message).with_class(Class::NotFound)
124}
125
126pub fn retryable(message: impl Into<Cow<'static, str>>) -> Message {
128 Message::new(message).with_class(Class::Retryable)
129}
130
131pub fn resource_exhaustion(kind: ResourceExhaustionKind, message: impl Into<Cow<'static, str>>) -> Message {
133 Message::new(message).with_class(Class::ResourceExhaustion(kind))
134}
135
136pub fn allocation_limit(message: impl Into<Cow<'static, str>>) -> Message {
138 resource_exhaustion(ResourceExhaustionKind::AllocationLimit, message)
139}
140
141pub fn allocation_failure(message: impl Into<Cow<'static, str>>) -> Message {
143 resource_exhaustion(ResourceExhaustionKind::AllocationFailure, message)
144}
145
146pub fn io(kind: std::io::ErrorKind, message: impl Into<Cow<'static, str>>) -> Message {
151 Message::new(message).with_class(Class::Io(kind))
152}
153
154#[derive(Clone, PartialEq)]
158#[non_exhaustive]
159pub enum MetadataValue {
160 Bool(bool),
162 I64(i64),
164 U64(u64),
166 F64(f64),
168 String(String),
170 Bytes(BString),
172 Path(PathBuf),
174}
175
176impl fmt::Debug for MetadataValue {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 match self {
179 MetadataValue::Bool(value) => write!(f, "Bool({value:?})"),
180 MetadataValue::I64(value) => write!(f, "I64({value:?})"),
181 MetadataValue::U64(value) => write!(f, "U64({value:?})"),
182 MetadataValue::F64(value) => write!(f, "F64({value:?})"),
183 MetadataValue::String(value) => write!(f, "String({value:?})"),
184 MetadataValue::Bytes(value) => write!(f, "Bytes({value:?})"),
185 MetadataValue::Path(value) => write!(f, "Path({value:?})"),
186 }
187 }
188}
189
190impl fmt::Display for MetadataValue {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 match self {
193 MetadataValue::Bool(value) => fmt::Display::fmt(value, f),
194 MetadataValue::I64(value) => fmt::Display::fmt(value, f),
195 MetadataValue::U64(value) => fmt::Display::fmt(value, f),
196 MetadataValue::F64(value) => fmt::Display::fmt(value, f),
197 MetadataValue::String(value) => fmt::Debug::fmt(value, f),
198 MetadataValue::Bytes(value) => fmt::Debug::fmt(value, f),
199 MetadataValue::Path(value) => fmt::Debug::fmt(value, f),
200 }
201 }
202}
203
204macro_rules! from {
205 ($variant:ident: $($ty:ty),+ $(,)?) => {
206 $(impl From<$ty> for MetadataValue {
207 fn from(value: $ty) -> Self {
208 Self::$variant(value.into())
209 }
210 })+
211 };
212}
213
214from!(Bool: bool);
215from!(I64: i8, i16, i32, i64);
216from!(U64: u8, u16, u32, u64);
217from!(F64: f32, f64);
218from!(String: String, &str);
219from!(Bytes: BString, &bstr::BStr, Vec<u8>, &[u8]);
220from!(Path: PathBuf, &std::path::Path);
221
222impl From<usize> for MetadataValue {
223 fn from(value: usize) -> Self {
224 Self::U64(value as u64)
225 }
226}
227
228impl From<isize> for MetadataValue {
229 fn from(value: isize) -> Self {
230 Self::I64(value as i64)
231 }
232}