1use std::borrow::Borrow;
2use std::collections::{btree_map, BTreeMap};
3use std::fmt;
4use std::str::FromStr;
5
6use rust_decimal::Decimal;
7use serde_json::{Number, Value};
8use time::OffsetDateTime;
9
10#[non_exhaustive]
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct NamespaceKey(String);
13
14impl NamespaceKey {
15 pub fn new(value: impl Into<String>) -> Result<Self, NamespaceKeyError> {
16 let value = value.into();
17 Self::validate(value.as_str())?;
18
19 Ok(Self(value))
20 }
21
22 pub fn validate(value: &str) -> Result<(), NamespaceKeyError> {
23 let Some((namespace, name)) = value.split_once('.') else {
24 return Err(NamespaceKeyError::MissingNamespace(value.to_owned()));
25 };
26
27 if namespace.is_empty() || name.is_empty() {
28 return Err(NamespaceKeyError::EmptySegment(value.to_owned()));
29 }
30
31 if namespace.contains('.') || name.contains('.') {
32 return Err(NamespaceKeyError::TooManySegments(value.to_owned()));
33 }
34
35 if !(namespace.bytes().all(Self::is_namespace_segment_byte)
36 && name.bytes().all(Self::is_namespace_segment_byte))
37 {
38 return Err(NamespaceKeyError::InvalidCharacters(value.to_owned()));
39 }
40
41 Ok(())
42 }
43
44 fn is_namespace_segment_byte(byte: u8) -> bool {
45 byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'_' || byte == b'-'
46 }
47}
48
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50#[non_exhaustive]
51#[derive(Debug, Clone, Default, PartialEq, Eq)]
52pub struct Extensions(BTreeMap<NamespaceKey, Value>);
53
54impl Extensions {
55 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn get(&self, key: impl AsRef<str>) -> Option<&Value> {
60 self.0.get(key.as_ref())
61 }
62
63 pub fn iter(&self) -> btree_map::Iter<'_, NamespaceKey, Value> {
64 self.0.iter()
65 }
66
67 pub fn is_empty(&self) -> bool {
68 self.0.is_empty()
69 }
70
71 pub fn as_map(&self) -> &BTreeMap<NamespaceKey, Value> {
72 &self.0
73 }
74
75 pub fn into_map(self) -> BTreeMap<NamespaceKey, Value> {
76 self.0
77 }
78
79 pub fn insert(&mut self, key: impl AsRef<str>, value: Value) -> Result<(), NamespaceKeyError> {
80 let namespace_key = NamespaceKey::new(key.as_ref())?;
81 self.0.insert(namespace_key, value);
82 Ok(())
83 }
84
85 pub fn insert_optional_string(
86 &mut self,
87 key: impl AsRef<str>,
88 value: Option<String>,
89 ) -> Result<(), NamespaceKeyError> {
90 if let Some(value) = value {
91 self.insert(key, Value::String(value))?;
92 }
93 Ok(())
94 }
95
96 pub fn insert_optional_bool(
97 &mut self,
98 key: impl AsRef<str>,
99 value: Option<bool>,
100 ) -> Result<(), NamespaceKeyError> {
101 if let Some(value) = value {
102 self.insert(key, Value::Bool(value))?;
103 }
104 Ok(())
105 }
106
107 pub fn insert_i64(
108 &mut self,
109 key: impl AsRef<str>,
110 value: i64,
111 ) -> Result<(), NamespaceKeyError> {
112 self.insert(key, Value::Number(Number::from(value)))
113 }
114
115 pub fn string(&self, key: impl AsRef<str>) -> Result<Option<String>, ExtensionValueError> {
116 let key = key.as_ref();
117 let Some(value) = self.0.get(key) else {
118 return Ok(None);
119 };
120 match value {
121 Value::String(value) => Ok(Some(value.clone())),
122 other => Err(ExtensionValueError::invalid_type(
123 key,
124 "string",
125 other.to_string(),
126 )),
127 }
128 }
129
130 pub fn i64(&self, key: impl AsRef<str>) -> Result<Option<i64>, ExtensionValueError> {
131 let key = key.as_ref();
132 let Some(value) = self.0.get(key) else {
133 return Ok(None);
134 };
135 match value {
136 Value::Number(number) => number
137 .as_i64()
138 .ok_or_else(|| {
139 ExtensionValueError::invalid_type(
140 key,
141 "integer value that fits into i64",
142 number.to_string(),
143 )
144 })
145 .map(Some),
146 Value::String(raw) => {
147 raw.parse::<i64>()
148 .map(Some)
149 .map_err(|err| ExtensionValueError::ParseInteger {
150 key: key.to_owned(),
151 value: raw.clone(),
152 message: err.to_string(),
153 })
154 }
155 other => Err(ExtensionValueError::invalid_type(
156 key,
157 "integer",
158 other.to_string(),
159 )),
160 }
161 }
162
163 pub fn i32(&self, key: impl AsRef<str>) -> Result<Option<i32>, ExtensionValueError> {
164 let key = key.as_ref();
165 self.i64(key)?
166 .map(|value| {
167 i32::try_from(value).map_err(|_| ExtensionValueError::IntegerOutOfRange {
168 key: key.to_owned(),
169 value,
170 target: "i32",
171 })
172 })
173 .transpose()
174 }
175
176 pub fn decimal(&self, key: impl AsRef<str>) -> Result<Option<Decimal>, ExtensionValueError> {
177 let key = key.as_ref();
178 let Some(value) = self.0.get(key) else {
179 return Ok(None);
180 };
181
182 let raw = match value {
183 Value::String(value) => value.clone(),
184 Value::Number(number) => number.to_string(),
185 other => {
186 return Err(ExtensionValueError::invalid_type(
187 key,
188 "decimal string or number",
189 other.to_string(),
190 ))
191 }
192 };
193
194 Decimal::from_str(&raw)
195 .map(Some)
196 .map_err(|err| ExtensionValueError::ParseDecimal {
197 key: key.to_owned(),
198 value: raw,
199 message: err.to_string(),
200 })
201 }
202
203 pub fn parse_optional_decimal<E>(
204 value: Option<String>,
205 on_error: impl FnOnce(rust_decimal::Error) -> E,
206 ) -> Result<Option<Decimal>, E> {
207 value
208 .map(|raw| Decimal::from_str(raw.as_ref()).map_err(on_error))
209 .transpose()
210 }
211
212 pub fn parse_timestamp<E>(
213 value: i64,
214 on_error: impl FnOnce(time::error::ComponentRange) -> E,
215 ) -> Result<OffsetDateTime, E> {
216 OffsetDateTime::from_unix_timestamp_nanos(i128::from(value) * 1_000_000).map_err(on_error)
217 }
218
219 pub fn parse_optional_timestamp<E>(
220 value: Option<i64>,
221 on_error: impl FnOnce(time::error::ComponentRange) -> E,
222 ) -> Result<Option<OffsetDateTime>, E> {
223 value
224 .filter(|timestamp| *timestamp >= 0)
225 .map(|timestamp| Self::parse_timestamp(timestamp, on_error))
226 .transpose()
227 }
228}
229
230impl From<BTreeMap<NamespaceKey, Value>> for Extensions {
231 fn from(value: BTreeMap<NamespaceKey, Value>) -> Self {
232 Self(value)
233 }
234}
235
236impl From<Extensions> for BTreeMap<NamespaceKey, Value> {
237 fn from(value: Extensions) -> Self {
238 value.0
239 }
240}
241
242#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
243#[non_exhaustive]
244#[derive(Debug, Clone, PartialEq, Eq)]
245pub enum ExtensionValueError {
246 InvalidType {
247 key: String,
248 expected: &'static str,
249 actual: String,
250 },
251 ParseInteger {
252 key: String,
253 value: String,
254 message: String,
255 },
256 IntegerOutOfRange {
257 key: String,
258 value: i64,
259 target: &'static str,
260 },
261 ParseDecimal {
262 key: String,
263 value: String,
264 message: String,
265 },
266}
267
268impl ExtensionValueError {
269 fn invalid_type(key: &str, expected: &'static str, actual: String) -> Self {
270 Self::InvalidType {
271 key: key.to_owned(),
272 expected,
273 actual,
274 }
275 }
276}
277
278impl TryFrom<&str> for NamespaceKey {
279 type Error = NamespaceKeyError;
280
281 fn try_from(value: &str) -> Result<Self, Self::Error> {
282 Self::new(value)
283 }
284}
285
286impl fmt::Display for NamespaceKey {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 f.write_str(&self.0)
289 }
290}
291
292impl Borrow<str> for NamespaceKey {
293 fn borrow(&self) -> &str {
294 &self.0
295 }
296}
297
298#[cfg(feature = "serde")]
299impl serde::Serialize for NamespaceKey {
300 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
301 where
302 S: serde::Serializer,
303 {
304 serializer.serialize_str(&self.0)
305 }
306}
307
308#[cfg(feature = "serde")]
309impl<'de> serde::Deserialize<'de> for NamespaceKey {
310 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
311 where
312 D: serde::Deserializer<'de>,
313 {
314 let value = <String as serde::Deserialize>::deserialize(deserializer)?;
315 Self::new(value).map_err(serde::de::Error::custom)
316 }
317}
318
319#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
320#[non_exhaustive]
321#[derive(Debug, Clone, PartialEq, Eq)]
322pub enum NamespaceKeyError {
323 MissingNamespace(String),
324 EmptySegment(String),
325 TooManySegments(String),
326 InvalidCharacters(String),
327}
328
329impl fmt::Display for ExtensionValueError {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 match self {
332 Self::InvalidType {
333 key,
334 expected,
335 actual,
336 } => write!(f, "extension `{key}` expected {expected}, got {actual}"),
337 Self::ParseInteger {
338 key,
339 value,
340 message,
341 } => write!(
342 f,
343 "extension `{key}` could not parse integer `{value}`: {message}"
344 ),
345 Self::IntegerOutOfRange { key, value, target } => {
346 write!(
347 f,
348 "extension `{key}` integer `{value}` does not fit into {target}"
349 )
350 }
351 Self::ParseDecimal {
352 key,
353 value,
354 message,
355 } => write!(
356 f,
357 "extension `{key}` could not parse decimal `{value}`: {message}"
358 ),
359 }
360 }
361}
362
363impl std::error::Error for ExtensionValueError {}
364
365impl fmt::Display for NamespaceKeyError {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 match self {
368 Self::MissingNamespace(value) => {
369 write!(f, "namespace key must be namespace.name: {value}")
370 }
371 Self::EmptySegment(value) => write!(f, "namespace key has an empty segment: {value}"),
372 Self::TooManySegments(value) => {
373 write!(f, "namespace key must contain exactly one dot: {value}")
374 }
375 Self::InvalidCharacters(value) => {
376 write!(f, "namespace key contains invalid characters: {value}")
377 }
378 }
379 }
380}
381
382impl std::error::Error for NamespaceKeyError {}