cairo_lang_filesystem/
cfg.rs

1use std::collections::BTreeSet;
2use std::fmt;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use smol_str::SmolStr;
6
7/// Option for the `#[cfg(...)]` language attribute.
8#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
9pub struct Cfg {
10    pub key: SmolStr,
11    pub value: Option<SmolStr>,
12}
13
14impl Cfg {
15    /// Creates an `cfg` option that is matchable as `#[cfg(name)]`.
16    pub fn name(name: impl Into<SmolStr>) -> Self {
17        Self { key: name.into(), value: None }
18    }
19
20    /// Creates an `cfg` option that is matchable as `#[cfg(key: "value")]`.
21    pub fn kv(key: impl Into<SmolStr>, value: impl Into<SmolStr>) -> Self {
22        Self { key: key.into(), value: Some(value.into()) }
23    }
24}
25
26impl fmt::Display for Cfg {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        write!(f, "{}", self.key)?;
29
30        if let Some(value) = &self.value {
31            write!(f, ": {value:?}")?;
32        }
33
34        Ok(())
35    }
36}
37
38impl fmt::Debug for Cfg {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        f.debug_tuple("Cfg").field(&DebugAsDisplay(&self)).finish()
41    }
42}
43
44mod serde_ext {
45    use serde::{Deserialize, Serialize};
46    use smol_str::SmolStr;
47
48    #[derive(Serialize, Deserialize)]
49    #[serde(untagged)]
50    pub enum Cfg {
51        KV(SmolStr, SmolStr),
52        Name(SmolStr),
53    }
54}
55
56impl Serialize for Cfg {
57    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
58        let sd = if let Some(value) = &self.value {
59            serde_ext::Cfg::KV(self.key.clone(), value.clone())
60        } else {
61            serde_ext::Cfg::Name(self.key.clone())
62        };
63        sd.serialize(serializer)
64    }
65}
66
67impl<'de> Deserialize<'de> for Cfg {
68    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
69        let sd = serde_ext::Cfg::deserialize(deserializer)?;
70        match sd {
71            serde_ext::Cfg::KV(k, v) => Ok(Cfg::kv(k, v)),
72            serde_ext::Cfg::Name(name) => Ok(Cfg::name(name)),
73        }
74    }
75}
76
77/// Set of `#[cfg(...)]` options.
78///
79/// Behaves like a multimap, i.e. it permits storing multiple values for the same key.
80/// This allows expressing, for example, the `feature` option that Rust/Cargo does.
81#[derive(Clone, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
82pub struct CfgSet(BTreeSet<Cfg>);
83
84impl CfgSet {
85    /// Creates an empty `CfgSet`.
86    ///
87    /// This function does not allocate.
88    pub fn new() -> Self {
89        Self(BTreeSet::new())
90    }
91
92    /// Returns the number of elements in the set.
93    pub fn len(&self) -> usize {
94        self.0.len()
95    }
96
97    /// Returns `true` if the set contains no elements.
98    pub fn is_empty(&self) -> bool {
99        self.0.is_empty()
100    }
101
102    /// Adds a value to the set.
103    pub fn insert(&mut self, cfg: Cfg) {
104        self.0.insert(cfg);
105    }
106
107    /// Combines two sets into new one.
108    pub fn union(&self, other: &Self) -> Self {
109        Self(self.0.union(&other.0).cloned().collect())
110    }
111
112    /// An iterator visiting all elements in insertion order.
113    pub fn iter(&self) -> impl Iterator<Item = &Cfg> {
114        self.0.iter()
115    }
116
117    /// Returns `true` if the set contains a value.
118    pub fn contains(&self, cfg: &Cfg) -> bool {
119        self.0.contains(cfg)
120    }
121
122    /// Returns `true` if the set is a subset of another,
123    /// i.e., `other` contains at least all the values in `self`.
124    pub fn is_subset(&self, other: &Self) -> bool {
125        self.0.is_subset(&other.0)
126    }
127
128    /// Returns `true` if the set is a superset of another,
129    /// i.e., `self` contains at least all the values in `other`.
130    pub fn is_superset(&self, other: &Self) -> bool {
131        other.is_subset(self)
132    }
133}
134
135impl IntoIterator for CfgSet {
136    type Item = Cfg;
137    type IntoIter = <BTreeSet<Cfg> as IntoIterator>::IntoIter;
138
139    fn into_iter(self) -> Self::IntoIter {
140        self.0.into_iter()
141    }
142}
143
144impl<'a> IntoIterator for &'a CfgSet {
145    type Item = &'a Cfg;
146    type IntoIter = <&'a BTreeSet<Cfg> as IntoIterator>::IntoIter;
147
148    fn into_iter(self) -> Self::IntoIter {
149        self.0.iter()
150    }
151}
152
153impl FromIterator<Cfg> for CfgSet {
154    fn from_iter<T: IntoIterator<Item = Cfg>>(iter: T) -> Self {
155        Self(FromIterator::from_iter(iter))
156    }
157}
158
159impl fmt::Debug for CfgSet {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        let mut tuple = f.debug_tuple("CfgSet");
162        for cfg in &self.0 {
163            tuple.field(&DebugAsDisplay(cfg));
164        }
165        tuple.finish()
166    }
167}
168
169struct DebugAsDisplay<T>(T);
170
171impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        fmt::Display::fmt(&self.0, f)
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use serde_json::json;
180
181    use crate::cfg::{Cfg, CfgSet};
182
183    #[test]
184    fn contains() {
185        let a = CfgSet::from_iter([Cfg::name("name"), Cfg::kv("k", "a"), Cfg::kv("k", "b")]);
186        assert!(a.contains(&Cfg::name("name")));
187        assert!(a.contains(&Cfg::kv("k", "a")));
188        assert!(a.contains(&Cfg::kv("k", "b")));
189        assert!(!a.contains(&Cfg::kv("k", "c")));
190    }
191
192    #[test]
193    fn is_superset() {
194        let a = CfgSet::from_iter([Cfg::name("name"), Cfg::kv("k", "a"), Cfg::kv("k", "b")]);
195        let b = CfgSet::from_iter([Cfg::name("name"), Cfg::kv("k", "a")]);
196        assert!(a.is_superset(&b));
197    }
198
199    #[test]
200    fn serde() {
201        let cfg = CfgSet::from_iter([
202            Cfg::name("name"),
203            Cfg::kv("k", "a"),
204            Cfg::name("name2"),
205            Cfg::kv("k", "b"),
206        ]);
207
208        let json = serde_json::to_value(&cfg).unwrap();
209
210        assert_eq!(json, json!([["k", "a"], ["k", "b"], "name", "name2"]));
211
212        let serde_cfg = serde_json::from_value::<CfgSet>(json).unwrap();
213
214        assert_eq!(serde_cfg, cfg);
215    }
216}