1use std::collections::{BTreeMap, HashMap, btree_map};
6use std::fmt;
7use std::ops::Index;
8use std::sync::Arc;
9
10#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct Metadata(
37 Option<Arc<BTreeMap<String, String>>>,
42);
43
44impl Metadata {
45 pub const fn new() -> Self {
49 Self(None)
50 }
51
52 pub fn len(&self) -> usize {
54 self.0.as_ref().map_or(0, |map| map.len())
55 }
56
57 pub fn is_empty(&self) -> bool {
59 self.0.is_none()
60 }
61
62 pub fn get(&self, key: &str) -> Option<&String> {
64 self.0.as_ref()?.get(key)
65 }
66
67 pub fn contains_key(&self, key: &str) -> bool {
69 self.0.as_ref().is_some_and(|map| map.contains_key(key))
70 }
71
72 pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) -> Option<String> {
78 Arc::make_mut(self.0.get_or_insert_default()).insert(key.into(), value.into())
79 }
80
81 #[must_use]
92 pub fn with(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
93 self.insert(key, value);
94 self
95 }
96
97 pub fn remove(&mut self, key: &str) -> Option<String> {
102 let map = self.0.as_mut()?;
103 if !map.contains_key(key) {
104 return None;
105 }
106 let removed = Arc::make_mut(map).remove(key);
107 if map.is_empty() {
108 self.0 = None;
109 }
110 removed
111 }
112
113 pub fn clear(&mut self) {
115 self.0 = None;
116 }
117
118 pub fn as_arc(&self) -> Option<&Arc<BTreeMap<String, String>>> {
123 self.0.as_ref()
124 }
125
126 pub fn into_arc(self) -> Option<Arc<BTreeMap<String, String>>> {
131 self.0
132 }
133
134 pub fn iter(&self) -> MetadataIter<'_> {
136 self.0
137 .as_deref()
138 .map(|map| map.iter())
139 .into_iter()
140 .flatten()
141 }
142
143 pub fn keys(&self) -> impl Iterator<Item = &String> {
145 self.iter().map(|(key, _)| key)
146 }
147
148 pub fn values(&self) -> impl Iterator<Item = &String> {
150 self.iter().map(|(_, value)| value)
151 }
152
153 pub fn retain<F>(&mut self, mut f: F)
160 where
161 F: FnMut(&String, &mut String) -> bool,
162 {
163 let Some(map) = self.0.as_mut() else { return };
164 Arc::make_mut(map).retain(&mut f);
165 if map.is_empty() {
166 self.0 = None;
167 }
168 }
169}
170
171pub type MetadataIter<'a> =
173 std::iter::Flatten<std::option::IntoIter<btree_map::Iter<'a, String, String>>>;
174
175impl fmt::Debug for Metadata {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 f.debug_map().entries(self.iter()).finish()
178 }
179}
180
181impl Index<&str> for Metadata {
182 type Output = String;
183
184 fn index(&self, key: &str) -> &String {
185 self.get(key)
186 .unwrap_or_else(|| panic!("no entry found for key {key:?}"))
187 }
188}
189
190impl From<BTreeMap<String, String>> for Metadata {
191 fn from(map: BTreeMap<String, String>) -> Self {
192 if map.is_empty() {
193 Self(None)
194 } else {
195 Self(Some(Arc::new(map)))
196 }
197 }
198}
199
200impl From<HashMap<String, String>> for Metadata {
201 fn from(map: HashMap<String, String>) -> Self {
202 map.into_iter().collect::<BTreeMap<_, _>>().into()
203 }
204}
205
206impl From<Arc<BTreeMap<String, String>>> for Metadata {
207 fn from(map: Arc<BTreeMap<String, String>>) -> Self {
208 if map.is_empty() {
209 Self(None)
210 } else {
211 Self(Some(map))
212 }
213 }
214}
215
216impl<K: Into<String>, V: Into<String>, const N: usize> From<[(K, V); N]> for Metadata {
217 fn from(entries: [(K, V); N]) -> Self {
218 entries.into_iter().collect()
219 }
220}
221
222impl From<Metadata> for BTreeMap<String, String> {
223 fn from(metadata: Metadata) -> Self {
224 match metadata.0 {
225 None => BTreeMap::new(),
226 Some(map) => Arc::try_unwrap(map).unwrap_or_else(|map| (*map).clone()),
227 }
228 }
229}
230
231impl From<Metadata> for HashMap<String, String> {
232 fn from(metadata: Metadata) -> Self {
233 metadata.into_iter().collect()
234 }
235}
236
237impl From<&Metadata> for HashMap<String, String> {
238 fn from(metadata: &Metadata) -> Self {
239 metadata
240 .iter()
241 .map(|(k, v)| (k.clone(), v.clone()))
242 .collect()
243 }
244}
245
246impl<K: Into<String>, V: Into<String>> FromIterator<(K, V)> for Metadata {
247 fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
248 iter.into_iter()
249 .map(|(k, v)| (k.into(), v.into()))
250 .collect::<BTreeMap<_, _>>()
251 .into()
252 }
253}
254
255impl<K: Into<String>, V: Into<String>> Extend<(K, V)> for Metadata {
256 fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
257 let mut iter = iter
258 .into_iter()
259 .map(|(k, v)| (k.into(), v.into()))
260 .peekable();
261 if iter.peek().is_none() {
262 return; }
264 Arc::make_mut(self.0.get_or_insert_default()).extend(iter);
265 }
266}
267
268impl<'a> IntoIterator for &'a Metadata {
269 type Item = (&'a String, &'a String);
270 type IntoIter = MetadataIter<'a>;
271
272 fn into_iter(self) -> Self::IntoIter {
273 self.iter()
274 }
275}
276
277impl IntoIterator for Metadata {
278 type Item = (String, String);
279 type IntoIter = btree_map::IntoIter<String, String>;
280
281 fn into_iter(self) -> Self::IntoIter {
282 BTreeMap::from(self).into_iter()
283 }
284}
285
286impl PartialEq<HashMap<String, String>> for Metadata {
287 fn eq(&self, other: &HashMap<String, String>) -> bool {
288 self.len() == other.len()
289 && self
290 .iter()
291 .all(|(k, v)| other.get(k).is_some_and(|other_v| v == other_v))
292 }
293}
294
295impl PartialEq<BTreeMap<String, String>> for Metadata {
296 fn eq(&self, other: &BTreeMap<String, String>) -> bool {
297 self.iter().eq(other.iter())
298 }
299}
300
301#[cfg(feature = "serde")]
302mod serde_impl {
303 use super::Metadata;
304 use std::collections::BTreeMap;
305
306 impl serde_core::Serialize for Metadata {
307 fn serialize<S: serde_core::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
308 use serde_core::ser::SerializeMap as _;
309 let mut map = serializer.serialize_map(Some(self.len()))?;
310 for (key, value) in self {
311 map.serialize_entry(key, value)?;
312 }
313 map.end()
314 }
315 }
316
317 impl<'de> serde_core::Deserialize<'de> for Metadata {
318 fn deserialize<D: serde_core::Deserializer<'de>>(
319 deserializer: D,
320 ) -> Result<Self, D::Error> {
321 Ok(BTreeMap::<String, String>::deserialize(deserializer)?.into())
322 }
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_empty() {
332 let metadata = Metadata::new();
333 assert!(metadata.is_empty());
334 assert_eq!(metadata.len(), 0);
335 assert_eq!(metadata.get("key"), None);
336 assert!(!metadata.contains_key("key"));
337 assert_eq!(metadata.iter().count(), 0);
338 assert_eq!(metadata, Metadata::default());
339
340 assert!(Metadata::from(HashMap::new()).0.is_none());
342 assert!(Metadata::from(BTreeMap::new()).0.is_none());
343 assert!(
344 std::iter::empty::<(String, String)>()
345 .collect::<Metadata>()
346 .0
347 .is_none()
348 );
349 }
350
351 #[test]
352 fn test_insert_get_remove() {
353 let mut metadata = Metadata::new();
354 assert_eq!(metadata.insert("a", "1"), None);
355 assert_eq!(metadata.insert("a", "2"), Some("1".to_string()));
356 assert_eq!(metadata.insert("b", "3"), None);
357
358 assert_eq!(metadata.len(), 2);
359 assert_eq!(metadata.get("a"), Some(&"2".to_string()));
360 assert_eq!(metadata["b"], "3");
361 assert!(metadata.contains_key("a"));
362 assert!(!metadata.contains_key("c"));
363
364 assert_eq!(metadata.remove("c"), None);
365 assert_eq!(metadata.remove("a"), Some("2".to_string()));
366 assert_eq!(metadata.remove("b"), Some("3".to_string()));
367
368 assert!(metadata.is_empty());
370 assert!(metadata.0.is_none());
371 }
372
373 #[test]
374 fn test_clear() {
375 let mut metadata = Metadata::from([("a", "1")]);
376 metadata.clear();
377 assert!(metadata.is_empty());
378 assert!(metadata.0.is_none());
379 }
380
381 #[test]
382 #[should_panic(expected = "no entry found for key \"missing\"")]
383 fn test_index_panics() {
384 let metadata = Metadata::from([("a", "1")]);
385 let _ = &metadata["missing"];
386 }
387
388 #[test]
389 fn test_copy_on_write() {
390 let mut metadata = Metadata::from([("a", "1")]);
391 let clone = metadata.clone();
392
393 let arc = metadata.0.as_ref().expect("non-empty");
395 assert!(Arc::ptr_eq(arc, clone.0.as_ref().expect("non-empty")));
396
397 metadata.insert("b", "2");
399 assert_eq!(metadata.len(), 2);
400 assert_eq!(clone.len(), 1);
401
402 let arc = metadata.0.as_ref().expect("non-empty").clone();
404 metadata.insert("c", "3");
405 assert!(
406 Arc::ptr_eq(&arc, metadata.0.as_ref().expect("non-empty"))
407 || Arc::strong_count(&arc) == 1
408 );
409
410 let clone = metadata.clone();
412 let arc = metadata.0.as_ref().expect("non-empty");
413 assert!(Arc::ptr_eq(arc, clone.0.as_ref().expect("non-empty")));
414 let mut metadata2 = metadata.clone();
415 assert_eq!(metadata2.remove("missing"), None);
416 assert!(Arc::ptr_eq(
417 metadata.0.as_ref().expect("non-empty"),
418 metadata2.0.as_ref().expect("non-empty")
419 ));
420 }
421
422 #[test]
423 fn test_deterministic_iteration_order() {
424 let metadata: Metadata = [("b", "2"), ("a", "1"), ("c", "3")].into_iter().collect();
425 let keys: Vec<&String> = metadata.keys().collect();
426 assert_eq!(keys, ["a", "b", "c"]);
427 let values: Vec<&String> = metadata.values().collect();
428 assert_eq!(values, ["1", "2", "3"]);
429 }
430
431 #[test]
432 fn test_eq_with_std_maps() {
433 let metadata = Metadata::from([("a", "1"), ("b", "2")]);
434
435 let hash_map: HashMap<String, String> = metadata.clone().into();
436 assert_eq!(metadata, hash_map);
437
438 let btree_map: BTreeMap<String, String> = metadata.clone().into();
439 assert_eq!(metadata, btree_map);
440
441 let mut different = hash_map.clone();
442 different.insert("c".to_string(), "3".to_string());
443 assert_ne!(metadata, different);
444
445 let mut different = hash_map;
446 different.insert("a".to_string(), "other".to_string());
447 assert_ne!(metadata, different);
448 }
449
450 #[test]
451 fn test_extend() {
452 let mut metadata = Metadata::new();
453 let clone = metadata.clone();
454 metadata.extend(std::iter::empty::<(String, String)>());
455 assert!(metadata.0.is_none()); metadata.extend([("a", "1"), ("b", "2")]);
458 assert_eq!(metadata.len(), 2);
459 assert!(clone.is_empty());
460 }
461
462 #[test]
463 fn test_as_arc_into_arc() {
464 let empty = Metadata::new();
465 assert!(empty.as_arc().is_none());
466 assert!(empty.into_arc().is_none());
467
468 let metadata = Metadata::from([("a", "1")]);
470 let arc_ref = metadata.as_arc().expect("non-empty");
471 assert_eq!(arc_ref.get("a").map(String::as_str), Some("1"));
472
473 let clone = metadata.clone();
474 assert!(Arc::ptr_eq(
476 metadata.as_arc().unwrap(),
477 clone.as_arc().unwrap()
478 ));
479
480 let arc = metadata.into_arc().expect("non-empty");
481 assert_eq!(arc.get("a").map(String::as_str), Some("1"));
482 assert!(Arc::ptr_eq(&arc, clone.as_arc().unwrap()));
484 }
485
486 #[test]
487 fn test_debug() {
488 let metadata = Metadata::from([("b", "2"), ("a", "1")]);
489 assert_eq!(format!("{metadata:?}"), r#"{"a": "1", "b": "2"}"#);
490 assert_eq!(format!("{:?}", Metadata::new()), "{}");
491 }
492
493 #[test]
494 fn test_retain() {
495 let mut metadata =
496 Metadata::from([("a", "1"), ("b", "2"), ("c", "3"), ("d", "4"), ("e", "5")]);
497
498 metadata.retain(|k, _| -> bool { k >= &String::from("c") });
499
500 let result_map = Metadata::from([("c", "3"), ("d", "4"), ("e", "5")]);
501 assert_eq!(metadata, result_map)
502 }
503
504 #[test]
505 fn test_retain_empty() {
506 let mut metadata = Metadata::new();
507 metadata.retain(|_, _| -> bool { true });
508 assert!(metadata.is_empty())
509 }
510
511 #[test]
512 fn test_retain_all_removed() {
513 let mut metadata = Metadata::from([("a", "1"), ("b", "2"), ("c", "3")]);
514 metadata.retain(|_, _| false);
515 assert!(metadata.is_empty());
516 assert!(metadata.0.is_none());
517 }
518
519 #[test]
520 fn test_retain_copy_on_write() {
521 let mut metadata = Metadata::from([("a", "1"), ("b", "2"), ("c", "3")]);
522 let clone = metadata.clone();
523
524 assert!(Arc::ptr_eq(
526 metadata.0.as_ref().expect("non-empty"),
527 clone.0.as_ref().expect("non-empty"),
528 ));
529
530 metadata.retain(|k, _| k != "a");
532 assert_eq!(metadata.len(), 2);
533 assert_eq!(clone.len(), 3);
534 assert!(!metadata.contains_key("a"));
535 assert!(clone.contains_key("a"));
536 }
537
538 #[test]
539 #[cfg(feature = "serde")]
540 fn test_serde_round_trip() {
541 for metadata in [Metadata::new(), Metadata::from([("a", "1"), ("b", "2")])] {
542 let serialized = postcard::to_stdvec(&metadata).expect("serialize");
543 let deserialized: Metadata = postcard::from_bytes(&serialized).expect("deserialize");
544 assert_eq!(metadata, deserialized);
545 }
546 }
547
548 #[test]
549 #[cfg(feature = "serde")]
550 fn test_serde_matches_std_map_format() {
551 let metadata = Metadata::from([("a", "1"), ("b", "2")]);
553 let as_btree_map: BTreeMap<String, String> = metadata.clone().into();
554
555 let serialized = postcard::to_stdvec(&metadata).expect("serialize");
556 let map_serialized = postcard::to_stdvec(&as_btree_map).expect("serialize");
557 assert_eq!(serialized, map_serialized);
558
559 let deserialized: Metadata = postcard::from_bytes(&map_serialized).expect("deserialize");
560 assert_eq!(metadata, deserialized);
561 }
562}