cairo_lang_filesystem/
cfg.rs

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