kanidmd_lib/valueset/
json.rs

1use crate::prelude::*;
2use crate::schema::SchemaAttribute;
3use crate::valueset::ScimResolveStatus;
4use crate::valueset::{DbValueSetV2, ValueSet};
5use kanidm_proto::internal::Filter as ProtoFilter;
6use smolset::SmolSet;
7
8#[derive(Debug, Clone)]
9pub struct ValueSetJsonFilter {
10    set: SmolSet<[ProtoFilter; 1]>,
11}
12
13impl ValueSetJsonFilter {
14    pub fn new(b: ProtoFilter) -> Box<Self> {
15        let mut set = SmolSet::new();
16        set.insert(b);
17        Box::new(ValueSetJsonFilter { set })
18    }
19
20    pub fn push(&mut self, b: ProtoFilter) -> bool {
21        self.set.insert(b)
22    }
23
24    pub fn from_dbvs2(data: &[String]) -> Result<ValueSet, OperationError> {
25        let set = data
26            .iter()
27            .map(|s| serde_json::from_str(s).map_err(|_| OperationError::SerdeJsonError))
28            .collect::<Result<_, _>>()?;
29        Ok(Box::new(ValueSetJsonFilter { set }))
30    }
31
32    // We need to allow this, because rust doesn't allow us to impl FromIterator on foreign
33    // types, and protofilter is foreign.
34    #[allow(clippy::should_implement_trait)]
35    pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
36    where
37        T: IntoIterator<Item = ProtoFilter>,
38    {
39        let set = iter.into_iter().collect();
40        Some(Box::new(ValueSetJsonFilter { set }))
41    }
42}
43
44impl ValueSetT for ValueSetJsonFilter {
45    fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
46        match value {
47            Value::JsonFilt(u) => Ok(self.set.insert(u)),
48            _ => {
49                debug_assert!(false);
50                Err(OperationError::InvalidValueState)
51            }
52        }
53    }
54
55    fn clear(&mut self) {
56        self.set.clear();
57    }
58
59    fn remove(&mut self, pv: &PartialValue, _cid: &Cid) -> bool {
60        match pv {
61            PartialValue::JsonFilt(u) => self.set.remove(u),
62            _ => {
63                debug_assert!(false);
64                true
65            }
66        }
67    }
68
69    fn contains(&self, pv: &PartialValue) -> bool {
70        match pv {
71            PartialValue::JsonFilt(u) => self.set.contains(u),
72            _ => false,
73        }
74    }
75
76    fn substring(&self, _pv: &PartialValue) -> bool {
77        false
78    }
79
80    fn startswith(&self, _pv: &PartialValue) -> bool {
81        false
82    }
83
84    fn endswith(&self, _pv: &PartialValue) -> bool {
85        false
86    }
87
88    fn lessthan(&self, _pv: &PartialValue) -> bool {
89        false
90    }
91
92    fn len(&self) -> usize {
93        self.set.len()
94    }
95
96    fn generate_idx_eq_keys(&self) -> Vec<String> {
97        self.set
98            .iter()
99            .map(|s| {
100                #[allow(clippy::expect_used)]
101                serde_json::to_string(s).expect("A json filter value was corrupted during run-time")
102            })
103            .collect()
104    }
105
106    fn syntax(&self) -> SyntaxType {
107        SyntaxType::JsonFilter
108    }
109
110    fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
111        true
112    }
113
114    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
115        Box::new(self.set.iter().filter_map(|i| {
116            serde_json::to_string(i)
117                .inspect_err(|err| {
118                    error!(?err, "A json filter value was corrupted during run-time")
119                })
120                .ok()
121        }))
122    }
123
124    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
125        Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
126            self.set
127                .iter()
128                .filter_map(|s| {
129                    serde_json::to_string(s)
130                        .inspect_err(|err| {
131                            error!(?err, "A json filter value was corrupted during run-time")
132                        })
133                        .ok()
134                })
135                .collect::<Vec<_>>(),
136        )))
137    }
138
139    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
140        DbValueSetV2::JsonFilter(
141            self.set
142                .iter()
143                .filter_map(|s| {
144                    serde_json::to_string(s)
145                        .inspect_err(|err| {
146                            error!(?err, "A json filter value was corrupted during run-time")
147                        })
148                        .ok()
149                })
150                .collect(),
151        )
152    }
153
154    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
155        Box::new(self.set.iter().cloned().map(PartialValue::JsonFilt))
156    }
157
158    fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
159        Box::new(self.set.iter().cloned().map(Value::JsonFilt))
160    }
161
162    fn equal(&self, other: &ValueSet) -> bool {
163        if let Some(other) = other.as_json_filter_set() {
164            &self.set == other
165        } else {
166            debug_assert!(false);
167            false
168        }
169    }
170
171    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
172        if let Some(b) = other.as_json_filter_set() {
173            mergesets!(self.set, b)
174        } else {
175            debug_assert!(false);
176            Err(OperationError::InvalidValueState)
177        }
178    }
179
180    fn to_json_filter_single(&self) -> Option<&ProtoFilter> {
181        if self.set.len() == 1 {
182            self.set.iter().take(1).next()
183        } else {
184            None
185        }
186    }
187
188    fn as_json_filter_set(&self) -> Option<&SmolSet<[ProtoFilter; 1]>> {
189        Some(&self.set)
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::{ProtoFilter, ValueSetJsonFilter};
196    use crate::prelude::{Attribute, ValueSet};
197
198    #[test]
199    fn test_scim_json_filter() {
200        let filter = ProtoFilter::Pres(Attribute::Class.to_string());
201        let vs: ValueSet = ValueSetJsonFilter::new(filter);
202
203        let data = r#"
204[
205  "{\"pres\":\"class\"}"
206]
207        "#;
208        crate::valueset::scim_json_reflexive(&vs, data);
209
210        // Test that we can parse json values into a valueset.
211        // crate::valueset::scim_json_put_reflexive::<ValueSetJsonFilter>(&vs, &[])
212    }
213}