str_ctx/
lib.rs

1#![no_std]
2#![deny(missing_docs)]
3//! Simple string context map.
4//!
5//! ```rust
6//! # use str_ctx::*;
7//! let ctx = StrCtx::from_iter([("hello", "zombies")]);
8//! assert_eq!("[hello:zombies]", &format!("{ctx}"));
9//!
10//! let ctx = ctx.derive([("friend", "apple")]);
11//! assert_eq!("[friend:apple,hello:zombies]", &format!("{ctx}"));
12//!
13//! let ctx = ctx.derive([("hello", "world")]);
14//! assert_eq!("[friend:apple,hello:world]", &format!("{ctx}"));
15//!
16//! let ctx: Vec<(&'static str, String)> = ctx.into_iter().collect();
17//! assert_eq!(
18//!     &[("friend", "apple".into()), ("hello", "world".into())],
19//!     ctx.as_slice(),
20//! );
21//! ```
22
23extern crate alloc;
24
25use alloc::collections::BTreeMap;
26use alloc::string::String;
27use alloc::string::ToString;
28use alloc::vec::Vec;
29use alloc::sync::Arc;
30use core::fmt;
31
32/// Simple string context map.
33#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct StrCtx(Arc<[(&'static str, String)]>);
35
36impl fmt::Debug for StrCtx {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        f.write_str("[")?;
39        let mut first = true;
40        for (k, v) in self.0.iter() {
41            if first {
42                first = false;
43            } else {
44                f.write_str(",")?;
45            }
46            f.write_str(k)?;
47            f.write_str(":")?;
48            f.write_str(v)?;
49        }
50        f.write_str("]")?;
51        Ok(())
52    }
53}
54
55impl fmt::Display for StrCtx {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        write!(f, "{:?}", self)
58    }
59}
60
61impl<V: fmt::Display> FromIterator<(&'static str, V)> for StrCtx {
62    fn from_iter<T>(iter: T) -> Self
63    where
64        T: IntoIterator<Item = (&'static str, V)>,
65    {
66        Self(BTreeMap::from_iter(iter.into_iter().map(|(k, v)| {
67            (k, v.to_string())
68        })).into_iter().collect::<Vec<_>>().into_boxed_slice().into())
69    }
70}
71
72impl IntoIterator for StrCtx {
73    type Item = (&'static str, String);
74    type IntoIter = <Vec<(&'static str, String)> as IntoIterator>::IntoIter;
75
76    fn into_iter(self) -> Self::IntoIter {
77        self.0.to_vec().into_iter()
78    }
79}
80
81impl<'a> IntoIterator for &'a StrCtx {
82    type Item = &'a (&'static str, String);
83    type IntoIter = core::slice::Iter<'a, (&'static str, String)>;
84
85    fn into_iter(self) -> Self::IntoIter {
86        self.0.into_iter()
87    }
88}
89
90impl StrCtx {
91    /// Derive a sub-context map by merging in additional k/v strings.
92    pub fn derive<V, T>(&self, iter: T) -> Self
93    where
94        V: fmt::Display,
95        T: IntoIterator<Item = (&'static str, V)>,
96    {
97        Self::from_iter(self.into_iter().cloned().chain(iter.into_iter().map(|(k, v)| {
98            (k, v.to_string())
99        })))
100    }
101}