kanidmd_lib/valueset/
iname.rs

1use crate::prelude::*;
2use crate::schema::SchemaAttribute;
3use crate::utils::trigraph_iter;
4use crate::valueset::ScimResolveStatus;
5use crate::valueset::{DbValueSetV2, ValueSet, ValueSetResolveStatus, ValueSetScimPut};
6use kanidm_proto::scim_v1::JsonValue;
7use std::cmp::Ordering;
8
9use std::collections::BTreeSet;
10
11#[derive(Debug, Clone)]
12pub struct ValueSetIname {
13    set: BTreeSet<String>,
14}
15
16impl ValueSetIname {
17    pub fn new(s: &str) -> Box<Self> {
18        let mut set = BTreeSet::new();
19        set.insert(s.to_lowercase());
20        Box::new(ValueSetIname { set })
21    }
22
23    pub fn push(&mut self, s: &str) -> bool {
24        self.set.insert(s.to_lowercase())
25    }
26
27    pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
28        let set = data.into_iter().collect();
29        Ok(Box::new(ValueSetIname { set }))
30    }
31
32    // We need to allow this, because rust doesn't allow us to impl FromIterator on foreign
33    // types, and str is foreign
34    #[allow(clippy::should_implement_trait)]
35    pub fn from_iter<'a, T>(iter: T) -> Option<Box<Self>>
36    where
37        T: IntoIterator<Item = &'a str>,
38    {
39        let set = iter.into_iter().map(str::to_string).collect();
40        Some(Box::new(ValueSetIname { set }))
41    }
42}
43
44impl ValueSetScimPut for ValueSetIname {
45    fn from_scim_json_put(value: JsonValue) -> Result<ValueSetResolveStatus, OperationError> {
46        let value = serde_json::from_value::<String>(value).map_err(|err| {
47            error!(?err, "SCIM Iname Syntax Invalid");
48            OperationError::SC0016InameSyntaxInvalid
49        })?;
50
51        let mut set = BTreeSet::new();
52        set.insert(value.to_lowercase());
53
54        Ok(ValueSetResolveStatus::Resolved(Box::new(ValueSetIname {
55            set,
56        })))
57    }
58}
59
60impl ValueSetT for ValueSetIname {
61    fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
62        match value {
63            Value::Iname(s) => Ok(self.set.insert(s)),
64            _ => {
65                debug_assert!(false);
66                Err(OperationError::InvalidValueState)
67            }
68        }
69    }
70
71    fn clear(&mut self) {
72        self.set.clear();
73    }
74
75    fn remove(&mut self, pv: &PartialValue, _cid: &Cid) -> bool {
76        match pv {
77            PartialValue::Iname(s) => self.set.remove(s),
78            _ => {
79                debug_assert!(false);
80                true
81            }
82        }
83    }
84
85    fn contains(&self, pv: &PartialValue) -> bool {
86        match pv {
87            PartialValue::Iname(s) => self.set.contains(s.as_str()),
88            _ => false,
89        }
90    }
91
92    fn substring(&self, pv: &PartialValue) -> bool {
93        match pv {
94            PartialValue::Iname(s2) => self.set.iter().any(|s1| s1.contains(s2)),
95            _ => {
96                debug_assert!(false);
97                false
98            }
99        }
100    }
101
102    fn startswith(&self, pv: &PartialValue) -> bool {
103        match pv {
104            PartialValue::Iname(s2) => self.set.iter().any(|s1| s1.starts_with(s2)),
105            _ => {
106                debug_assert!(false);
107                false
108            }
109        }
110    }
111
112    fn endswith(&self, pv: &PartialValue) -> bool {
113        match pv {
114            PartialValue::Iname(s2) => self.set.iter().any(|s1| s1.ends_with(s2)),
115            _ => {
116                debug_assert!(false);
117                false
118            }
119        }
120    }
121
122    fn lessthan(&self, _pv: &PartialValue) -> bool {
123        false
124    }
125
126    fn len(&self) -> usize {
127        self.set.len()
128    }
129
130    fn generate_idx_eq_keys(&self) -> Vec<String> {
131        self.set.iter().cloned().collect()
132    }
133
134    fn generate_idx_sub_keys(&self) -> Vec<String> {
135        let lower: Vec<_> = self.set.iter().map(|s| s.to_lowercase()).collect();
136        let mut trigraphs: Vec<_> = lower.iter().flat_map(|v| trigraph_iter(v)).collect();
137
138        trigraphs.sort_unstable();
139        trigraphs.dedup();
140
141        trigraphs.into_iter().map(String::from).collect()
142    }
143
144    fn syntax(&self) -> SyntaxType {
145        SyntaxType::Utf8StringIname
146    }
147
148    fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
149        self.set.iter().all(|s| {
150            Value::validate_str_escapes(s)
151                && Value::validate_singleline(s)
152                && Value::validate_iname(s.as_str())
153        })
154    }
155
156    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
157        Box::new(self.set.iter().cloned())
158    }
159
160    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
161        let mut iter = self.set.iter().cloned();
162        if self.len() == 1 {
163            let v = iter.next().unwrap_or_default();
164            Some(v.into())
165        } else {
166            let arr = iter.collect::<Vec<_>>();
167            Some(arr.into())
168        }
169    }
170
171    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
172        DbValueSetV2::Iname(self.set.iter().cloned().collect())
173    }
174
175    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
176        Box::new(self.set.iter().map(|i| PartialValue::new_iname(i.as_str())))
177    }
178
179    fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
180        Box::new(self.set.iter().map(|i| Value::new_iname(i.as_str())))
181    }
182
183    fn equal(&self, other: &ValueSet) -> bool {
184        if let Some(other) = other.as_iname_set() {
185            &self.set == other
186        } else {
187            debug_assert!(false);
188            false
189        }
190    }
191
192    fn cmp(&self, other: &ValueSet) -> Ordering {
193        if let Some(other) = other.as_iname_set() {
194            self.set.cmp(other)
195        } else {
196            debug_assert!(false);
197            Ordering::Equal
198        }
199    }
200
201    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
202        if let Some(b) = other.as_iname_set() {
203            mergesets!(self.set, b)
204        } else {
205            debug_assert!(false);
206            Err(OperationError::InvalidValueState)
207        }
208    }
209
210    fn to_iname_single(&self) -> Option<&str> {
211        if self.set.len() == 1 {
212            self.set.iter().take(1).next().map(|s| s.as_str())
213        } else {
214            None
215        }
216    }
217
218    fn as_iname_set(&self) -> Option<&BTreeSet<String>> {
219        Some(&self.set)
220    }
221
222    fn as_iname_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
223        Some(Box::new(self.set.iter().map(|s| s.as_str())))
224    }
225
226    fn migrate_iutf8_iname(&self) -> Result<Option<ValueSet>, OperationError> {
227        Ok(None)
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::ValueSetIname;
234    use crate::prelude::ValueSet;
235
236    #[test]
237    fn test_scim_iname() {
238        let vs: ValueSet = ValueSetIname::new("stevo");
239        crate::valueset::scim_json_reflexive(&vs, r#""stevo""#);
240
241        // Test that we can parse json values into a valueset.
242        crate::valueset::scim_json_put_reflexive::<ValueSetIname>(&vs, &[])
243    }
244}