scratchstack_aws_principal/
session.rs

1use {
2    base64::{engine::general_purpose::STANDARD as BASE64_ENGINE, Engine},
3    chrono::{DateTime, FixedOffset, Utc},
4    std::{
5        collections::{
6            hash_map::{Drain, Entry, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, ValuesMut},
7            HashMap, TryReserveError,
8        },
9        fmt::{Display, Formatter, Result as FmtResult},
10        hash::Hash,
11        iter::{Extend, FromIterator, IntoIterator},
12        net::{IpAddr, Ipv4Addr, Ipv6Addr},
13        ops::Index,
14        panic::UnwindSafe,
15    },
16};
17
18/// Associated data about a principal. This is a map of ASCII case-insensitive strings to [SessionValue] values.
19///
20/// This wraps the standard Rust [HashMap] type, providing the case-insensitive key lookup and setting values to
21/// the [SessionValue] type.
22#[derive(Clone, Debug)]
23pub struct SessionData {
24    /// The variables associated with the session with the keys lower-cased.
25    variables: HashMap<String, SessionValue>,
26}
27
28impl SessionData {
29    /// Creates an empty HashMap.
30    ///
31    /// The underlying hash map is initially created with a capacity of 0, so it will not allocate until it is first
32    /// inserted into.
33    pub fn new() -> Self {
34        Self {
35            variables: HashMap::new(),
36        }
37    }
38
39    /// Create a new session data object with a pre-allocated capacity.
40    pub fn with_capacity(capacity: usize) -> Self {
41        Self {
42            variables: HashMap::with_capacity(capacity),
43        }
44    }
45
46    /// Returns the number of elements the map can hold without reallocating.
47    ///
48    /// This number is a lower bound; the [SessionData] might be able to hold more, but is guaranteed to be able to
49    /// hold at least this many.
50    pub fn capacity(&self) -> usize {
51        self.variables.capacity()
52    }
53
54    /// Clears the map, removing all key-value pairs. Keeps the allocated memory for reuse.
55    pub fn clear(&mut self) {
56        self.variables.clear();
57    }
58
59    /// Returns `true` if the map contains a value for the specified key.
60    pub fn contains_key<Q: AsRef<str> + ?Sized>(&self, k: &Q) -> bool {
61        self.variables.contains_key(&k.as_ref().to_lowercase())
62    }
63
64    /// Clears the map, returning all key-value pairs as an iterator. Keeps the allocated memory for reuse.
65    ///
66    /// If the returned iterator is dropped before being fully consumed, it drops the remaining key-value pairs. The
67    /// returned iterator keeps a mutable borrow on the map to optimize its implementation.
68    pub fn drain(&mut self) -> Drain<'_, String, SessionValue> {
69        self.variables.drain()
70    }
71
72    /// Gets the given key’s corresponding entry in the map for in-place manipulation.
73    pub fn entry<Q: AsRef<str> + ?Sized>(&mut self, key: &Q) -> Entry<'_, String, SessionValue> {
74        self.variables.entry(key.as_ref().to_lowercase())
75    }
76
77    /// Returns a reference to the value corresponding to the key.
78    pub fn get<Q: AsRef<str> + ?Sized>(&self, key: &Q) -> Option<&SessionValue> {
79        self.variables.get(&key.as_ref().to_lowercase())
80    }
81
82    /// Returns the key-value pair corresponding to the supplied key.
83    pub fn get_key_value<Q: AsRef<str> + ?Sized>(&self, key: &Q) -> Option<(&str, &SessionValue)> {
84        self.variables.get_key_value(&key.as_ref().to_lowercase()).map(|(key, value)| (key.as_str(), value))
85    }
86
87    /// Returns a mutable reference to the value corresponding to the key.
88    pub fn get_mut<Q: AsRef<str> + ?Sized>(&mut self, key: &Q) -> Option<&mut SessionValue> {
89        self.variables.get_mut(&key.as_ref().to_lowercase())
90    }
91
92    /// Inserts a key-value pair into the map.
93    ///
94    /// If the map did not have this key present, None is returned.
95    /// If the map did have this key present, the value is updated, and the old value is returned.
96    pub fn insert<Q: AsRef<str> + ?Sized>(&mut self, key: &Q, value: SessionValue) -> Option<SessionValue> {
97        self.variables.insert(key.as_ref().to_lowercase(), value)
98    }
99
100    /// Creates a consuming iterator visiting all the keys in arbitrary order. The map cannot be used after calling
101    /// this. The iterator element type is `String`.
102    pub fn into_keys(self) -> IntoKeys<String, SessionValue> {
103        self.variables.into_keys()
104    }
105
106    /// Creates a consuming iterator visiting all the values in arbitrary order. The map cannot be used after calling
107    /// this. The iterator element type is `SessionValue`.
108    pub fn into_values(self) -> IntoValues<String, SessionValue> {
109        self.variables.into_values()
110    }
111
112    /// An iterator visiting all key-value pairs in arbitrary order. The iterator element type is
113    /// `(&'a String, &'a SessionData)`.
114    pub fn iter(&self) -> Iter<'_, String, SessionValue> {
115        self.variables.iter()
116    }
117
118    /// An iterator visiting all key-value pairs in arbitrary order, with mutable references to the values. The
119    /// iterator element type is `(&'a String, &'a mut SessionValue)`.
120    pub fn iter_mut(&mut self) -> IterMut<'_, String, SessionValue> {
121        self.variables.iter_mut()
122    }
123
124    /// Returns `true` if the map contains no elements.
125    pub fn is_empty(&self) -> bool {
126        self.variables.is_empty()
127    }
128
129    /// An iterator visiting all keys in arbitrary order. The iterator element type is `&'a String`.
130    pub fn keys(&self) -> Keys<'_, String, SessionValue> {
131        self.variables.keys()
132    }
133
134    /// Returns the number of elements in the map.
135    pub fn len(&self) -> usize {
136        self.variables.len()
137    }
138
139    /// Removes a key from the map, returning the value at the key if the key was previously in the map.
140    pub fn remove<Q: AsRef<str> + ?Sized>(&mut self, key: &Q) -> Option<SessionValue> {
141        self.variables.remove(&key.as_ref().to_lowercase())
142    }
143
144    /// Removes a key from the map, returning the stored key and value if the key was previously in the map.
145    pub fn remove_entry<Q: AsRef<str> + ?Sized>(&mut self, key: &Q) -> Option<(String, SessionValue)> {
146        self.variables.remove_entry(&key.as_ref().to_lowercase())
147    }
148
149    /// Reserves capacity for at least `additional` more elements to be inserted in the `SessionData`. The collection
150    /// may reserve more space to speculatively avoid frequent reallocations. After calling `reserve`, capacity will be
151    /// greater than or equal to `self.len() + additional`. Does nothing if capacity is already sufficient.
152    ///
153    /// # Panics
154    ///
155    /// Panics if the new allocation size overflows [`usize`].
156    pub fn reserve(&mut self, additional: usize) {
157        self.variables.reserve(additional)
158    }
159
160    /// Retains only the elements specified by the predicate.
161    ///
162    /// In other words, remove all pairs `(k, v)` for which `f(&k, &mut v)` returns `false`. The elements are visited
163    /// in unsorted (and unspecified) order.
164    pub fn retain<F: FnMut(&str, &mut SessionValue) -> bool>(&mut self, mut f: F) {
165        self.variables.retain(|key, value| f(key.as_str(), value))
166    }
167
168    /// Shrinks the capacity of the map with a lower limit. It will drop down no lower than the supplied limit while
169    /// maintaining the internal rules and possibly leaving some space in accordance with the resize policy.
170    ///
171    /// If the current capacity is less than the lower limit, this is a no-op.
172    pub fn shrink_to(&mut self, min_capacity: usize) {
173        self.variables.shrink_to(min_capacity)
174    }
175
176    /// Shrinks the capacity of the map as much as possible. It will drop down as much as possible while maintaining
177    /// the internal rules and possibly leaving some space in accordance with the resize policy.
178    pub fn shrink_to_fit(&mut self) {
179        self.variables.shrink_to_fit()
180    }
181
182    /// Tries to reserve capacity for at least `additional` more elements to be inserted in the `SessionData`. The
183    /// collection may reserve more space to speculatively avoid frequent reallocations. After calling `try_reserve`,
184    /// capacity will be greater than or equal to `self.len() + additional` if it returns `Ok(())`. Does nothing if
185    /// capacity is already sufficient.
186    ///
187    /// # Errors
188    ///
189    /// If the capacity overflows, or the allocator reports a failure, then an error is returned.
190    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
191        self.variables.try_reserve(additional)
192    }
193
194    /// An iterator visiting all values in arbitrary order. The iterator element type is `&'a SessionValue`.
195    pub fn values(&self) -> Values<'_, String, SessionValue> {
196        self.variables.values()
197    }
198
199    /// An iterator visiting all values mutably in arbitrary order. The iterator element type is `&'a mut SessionValue`.
200    pub fn values_mut(&mut self) -> ValuesMut<'_, String, SessionValue> {
201        self.variables.values_mut()
202    }
203}
204
205impl Default for SessionData {
206    fn default() -> Self {
207        Self::new()
208    }
209}
210
211impl<'a, K: AsRef<str> + ?Sized> Extend<(&'a K, &'a SessionValue)> for SessionData {
212    fn extend<T: IntoIterator<Item = (&'a K, &'a SessionValue)>>(&mut self, iter: T) {
213        self.variables.extend(iter.into_iter().map(|(key, value)| (key.as_ref().to_lowercase(), value.clone())));
214    }
215}
216
217impl From<HashMap<String, SessionValue>> for SessionData {
218    fn from(variables: HashMap<String, SessionValue>) -> Self {
219        let mut my_vars = HashMap::new();
220        for (key, value) in variables.iter() {
221            my_vars.insert(key.to_lowercase(), value.clone());
222        }
223
224        Self {
225            variables: my_vars,
226        }
227    }
228}
229
230impl<K: AsRef<str>, const N: usize> From<[(K, SessionValue); N]> for SessionData {
231    fn from(variables: [(K, SessionValue); N]) -> Self {
232        let mut my_vars = HashMap::new();
233        for (key, value) in variables.iter() {
234            my_vars.insert(key.as_ref().to_lowercase(), value.clone());
235        }
236
237        Self {
238            variables: my_vars,
239        }
240    }
241}
242
243impl<K: AsRef<str>> FromIterator<(K, SessionValue)> for SessionData {
244    fn from_iter<T: IntoIterator<Item = (K, SessionValue)>>(iter: T) -> Self {
245        let mut my_vars = HashMap::new();
246        for (key, value) in iter {
247            my_vars.insert(key.as_ref().to_lowercase(), value.clone());
248        }
249
250        Self {
251            variables: my_vars,
252        }
253    }
254}
255
256impl<Q: AsRef<str> + ?Sized> Index<&'_ Q> for SessionData {
257    type Output = SessionValue;
258
259    fn index(&self, key: &Q) -> &Self::Output {
260        self.variables.get(&key.as_ref().to_lowercase()).unwrap()
261    }
262}
263
264impl<'a> IntoIterator for &'a SessionData {
265    type Item = (&'a String, &'a SessionValue);
266    type IntoIter = Iter<'a, String, SessionValue>;
267
268    fn into_iter(self) -> Self::IntoIter {
269        self.variables.iter()
270    }
271}
272
273impl<'a> IntoIterator for &'a mut SessionData {
274    type Item = (&'a String, &'a mut SessionValue);
275    type IntoIter = IterMut<'a, String, SessionValue>;
276
277    fn into_iter(self) -> Self::IntoIter {
278        self.variables.iter_mut()
279    }
280}
281
282impl IntoIterator for SessionData {
283    type Item = (String, SessionValue);
284    type IntoIter = IntoIter<String, SessionValue>;
285
286    fn into_iter(self) -> Self::IntoIter {
287        self.variables.into_iter()
288    }
289}
290
291impl UnwindSafe for SessionData {}
292
293impl PartialEq for SessionData {
294    fn eq(&self, other: &Self) -> bool {
295        self.variables == other.variables
296    }
297}
298
299impl PartialEq<HashMap<String, SessionValue>> for SessionData {
300    fn eq(&self, other: &HashMap<String, SessionValue>) -> bool {
301        if self.variables.len() != other.len() {
302            return false;
303        }
304
305        for (key, other_value) in other.iter() {
306            match self.variables.get(&key.to_lowercase()) {
307                None => return false,
308                Some(value) => {
309                    if value != other_value {
310                        return false;
311                    }
312                }
313            }
314        }
315
316        true
317    }
318}
319
320impl Eq for SessionData {}
321
322/// Associated data about a session key.
323#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
324pub enum SessionValue {
325    /// Null value
326    Null,
327
328    /// Binary value
329    Binary(Vec<u8>),
330
331    /// Boolean value
332    Bool(bool),
333
334    /// Integer value
335    Integer(i64),
336
337    /// IP address value
338    IpAddr(IpAddr),
339
340    /// String value
341    String(String),
342
343    /// Timestamp value
344    Timestamp(DateTime<Utc>),
345}
346
347impl SessionValue {
348    /// Indicates whether this is a null value.
349    #[inline]
350    pub fn is_null(&self) -> bool {
351        matches!(self, Self::Null)
352    }
353
354    /// Returns the session value as a variable subtitution in an Aspen policy.
355    pub fn as_variable_value(&self) -> String {
356        match self {
357            Self::Null => "".to_string(),
358            Self::Binary(value) => BASE64_ENGINE.encode(value),
359            Self::Bool(b) => if *b {
360                "true"
361            } else {
362                "false"
363            }
364            .to_string(),
365            Self::Integer(i) => format!("{i}"),
366            Self::IpAddr(ip) => format!("{ip}"),
367            Self::String(s) => s.clone(),
368            Self::Timestamp(t) => format!("{}", t.format("%Y-%m-%dT%H:%M:%SZ")),
369        }
370    }
371}
372
373impl From<bool> for SessionValue {
374    fn from(value: bool) -> Self {
375        Self::Bool(value)
376    }
377}
378
379impl From<i64> for SessionValue {
380    fn from(value: i64) -> Self {
381        Self::Integer(value)
382    }
383}
384
385impl From<IpAddr> for SessionValue {
386    fn from(value: IpAddr) -> Self {
387        Self::IpAddr(value)
388    }
389}
390
391impl From<Ipv4Addr> for SessionValue {
392    fn from(value: Ipv4Addr) -> Self {
393        Self::IpAddr(IpAddr::V4(value))
394    }
395}
396
397impl From<Ipv6Addr> for SessionValue {
398    fn from(value: Ipv6Addr) -> Self {
399        Self::IpAddr(IpAddr::V6(value))
400    }
401}
402
403impl From<&str> for SessionValue {
404    fn from(value: &str) -> Self {
405        Self::String(value.to_string())
406    }
407}
408
409impl From<String> for SessionValue {
410    fn from(value: String) -> Self {
411        Self::String(value)
412    }
413}
414
415impl From<DateTime<FixedOffset>> for SessionValue {
416    fn from(value: DateTime<FixedOffset>) -> Self {
417        Self::Timestamp(value.into())
418    }
419}
420
421impl From<DateTime<Utc>> for SessionValue {
422    fn from(value: DateTime<Utc>) -> Self {
423        Self::Timestamp(value)
424    }
425}
426
427impl Display for SessionValue {
428    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
429        match self {
430            Self::Null => f.write_str("null"),
431            Self::Binary(value) => f.write_str(&BASE64_ENGINE.encode(value)),
432            Self::Bool(b) => Display::fmt(b, f),
433            Self::Integer(i) => Display::fmt(i, f),
434            Self::IpAddr(ip) => Display::fmt(ip, f),
435            Self::String(s) => f.write_str(s),
436            Self::Timestamp(t) => write!(f, "{}", t.format("%Y-%m-%dT%H:%M:%SZ")),
437        }
438    }
439}
440
441#[cfg(test)]
442mod tests {
443    use {
444        super::{SessionData, SessionValue},
445        chrono::{DateTime, FixedOffset, NaiveDate, Utc},
446        std::{
447            cmp::Ordering,
448            collections::hash_map::{DefaultHasher, HashMap},
449            hash::{Hash, Hasher},
450            iter::IntoIterator,
451            net::{IpAddr, Ipv4Addr, Ipv6Addr},
452        },
453    };
454
455    #[test]
456    fn check_session_value_derived() {
457        let sv1a = SessionValue::Null;
458        let sv1b = SessionValue::Null;
459        let sv2 = SessionValue::Bool(true);
460        let values = vec![
461            SessionValue::Null,
462            SessionValue::Bool(false),
463            SessionValue::Bool(true),
464            SessionValue::Integer(-1),
465            SessionValue::Integer(0),
466            SessionValue::Integer(1),
467            SessionValue::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
468            SessionValue::IpAddr(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
469            SessionValue::String("test1".to_string()),
470            SessionValue::String("test2".to_string()),
471        ];
472        let display = ["null", "false", "true", "-1", "0", "1", "127.0.0.1", "::1", "test1", "test2"];
473        assert_eq!(sv1a, sv1b);
474        assert_ne!(sv1a, sv2);
475        assert_eq!(sv1a, sv1a.clone());
476
477        // Ensure session values are hashable.
478        let mut h1a = DefaultHasher::new();
479        let mut h1b = DefaultHasher::new();
480        let mut h2 = DefaultHasher::new();
481        sv1a.hash(&mut h1a);
482        sv1b.hash(&mut h1b);
483        sv2.hash(&mut h2);
484        let hash1a = h1a.finish();
485        let hash1b = h1b.finish();
486        let hash2 = h2.finish();
487        assert_eq!(hash1a, hash1b);
488        assert_ne!(hash1a, hash2);
489
490        // Ensure a logical ordering for session values.
491        for i in 0..values.len() {
492            for j in 0..values.len() {
493                assert_eq!(values[i].cmp(&values[j]), i.cmp(&j));
494
495                match i.cmp(&j) {
496                    Ordering::Less => assert!(values[i] < values[j]),
497                    Ordering::Equal => assert!(values[i] == values[j]),
498                    Ordering::Greater => assert!(values[i] > values[j]),
499                }
500            }
501        }
502
503        // Ensure we can debug print session values.
504        for ref value in values.iter() {
505            let _ = format!("{value:?}");
506        }
507
508        // Ensure the display matches
509        for i in 0..values.len() {
510            assert_eq!(values[i].to_string(), display[i]);
511        }
512    }
513
514    #[test]
515    fn check_case_sensitivity() {
516        let mut sd = SessionData::new();
517        assert!(sd.is_empty());
518        sd.insert("test", SessionValue::Null);
519        assert!(sd.contains_key("TEST"));
520        assert!(sd.contains_key("test"));
521
522        sd.insert("Test", SessionValue::Bool(true));
523        sd.insert("TeST2", SessionValue::Integer(100));
524        assert!(sd.contains_key("tEsT"));
525        assert!(sd.contains_key("test"));
526
527        assert_eq!(sd.len(), 2);
528        assert!(!sd.is_empty());
529        assert_eq!(sd.get("test"), Some(&SessionValue::Bool(true)));
530        assert!(sd.contains_key("tEsT"));
531        assert!(sd.contains_key("test"));
532        assert_eq!(sd.get_key_value("tEST"), Some(("test", &SessionValue::Bool(true))));
533
534        let sv = sd.get_mut("tesT");
535        assert!(sv.is_some());
536        *sv.unwrap() = SessionValue::String("Hello".to_string());
537        assert_eq!(sd.get("test"), Some(&SessionValue::String("Hello".to_string())));
538
539        sd.entry("test2").and_modify(|v| *v = SessionValue::Integer(200));
540        assert_eq!(sd["test2"], SessionValue::Integer(200));
541
542        assert!(sd.remove("teST").is_some());
543        assert!(sd.remove("test").is_none());
544        assert!(sd.remove_entry("test2").is_some());
545    }
546
547    #[test]
548    fn check_clone_eq() {
549        let mut sd1 = SessionData::new();
550        sd1.insert("test", SessionValue::String("Hello World".to_string()));
551        let sd2 = sd1.clone();
552        assert_eq!(sd1, sd2);
553
554        let mut sd3 = SessionData::with_capacity(1);
555        assert!(sd3.capacity() > 0);
556        sd3.insert("TEST", SessionValue::String("Hello World".to_string()));
557        assert_eq!(sd1, sd3);
558        sd3.drain();
559        assert_ne!(sd1, sd3);
560        sd3.insert("test", SessionValue::String("Hello again".to_string()));
561        assert_ne!(sd1, sd3);
562        sd3.insert("test", SessionValue::Integer(1));
563        assert_ne!(sd1, sd3);
564        sd3.clear();
565        sd3.insert("TeST", SessionValue::String("Hello World".to_string()));
566        assert_eq!(sd1, sd3);
567
568        let mut h = HashMap::with_capacity(1);
569        h.insert("test".to_string(), SessionValue::String("Hello World".to_string()));
570        assert_eq!(sd1, h);
571        h.drain();
572        assert_ne!(sd1, h);
573        h.insert("test".to_string(), SessionValue::String("Hello again".to_string()));
574        assert_ne!(sd1, h);
575        h.insert("test".to_string(), SessionValue::Integer(1));
576        assert_ne!(sd1, h);
577        h.insert("test".to_string(), SessionValue::String("Hello World".to_string()));
578        assert_eq!(sd1, h);
579        h.drain();
580        h.insert("test2".to_string(), SessionValue::String("Hello World".to_string()));
581        assert_ne!(sd1, h);
582
583        sd1.clear();
584        sd1.shrink_to_fit();
585        assert_eq!(sd1.capacity(), 0);
586        sd1.reserve(100);
587        assert!(sd1.capacity() >= 100);
588        sd1.shrink_to(50);
589        assert!(sd1.capacity() >= 50);
590
591        // Ensure we can debug print session data.
592        let _ = format!("{sd1:?}");
593    }
594
595    #[test]
596    fn check_from_hashmap() {
597        let mut h = HashMap::new();
598        h.insert("Test".to_string(), SessionValue::String("Hello World".to_string()));
599        let mut sd = SessionData::from(h);
600        assert_eq!(sd.len(), 1);
601        assert_eq!(sd["test"], SessionValue::String("Hello World".to_string()));
602
603        let to_add = [("Test2", SessionValue::Integer(100)), ("Test3", SessionValue::Bool(true))];
604
605        sd.extend(to_add.iter().map(|r| (r.0, &r.1)));
606        assert_eq!(sd.len(), 3);
607        assert_eq!(sd["test2"], SessionValue::Integer(100));
608        assert_eq!(sd["test3"], SessionValue::Bool(true));
609    }
610
611    #[test]
612    fn check_from_array() {
613        let values: [(String, SessionValue); 3] = [
614            ("Test".to_string(), SessionValue::String("Hello World".to_string())),
615            ("Test2".to_string(), SessionValue::Integer(100)),
616            ("Test3".to_string(), SessionValue::Bool(true)),
617        ];
618        let sd = SessionData::from(values);
619        assert_eq!(sd.len(), 3);
620        assert_eq!(sd["test"], SessionValue::String("Hello World".to_string()));
621        assert_eq!(sd["test2"], SessionValue::Integer(100));
622        assert_eq!(sd["test3"], SessionValue::Bool(true));
623    }
624
625    #[test]
626    fn check_from_iter() {
627        let values = vec![
628            ("Test".to_string(), SessionValue::String("Hello World".to_string())),
629            ("Test2".to_string(), SessionValue::Integer(100)),
630            ("Test3".to_string(), SessionValue::Bool(true)),
631        ];
632        let sd = SessionData::from_iter(values);
633        assert_eq!(sd.len(), 3);
634        assert_eq!(sd["test"], SessionValue::String("Hello World".to_string()));
635        assert_eq!(sd["test2"], SessionValue::Integer(100));
636        assert_eq!(sd["test3"], SessionValue::Bool(true));
637    }
638
639    #[test]
640    fn check_keys() {
641        let mut sd: SessionData = Default::default();
642        sd.try_reserve(3).unwrap();
643        sd.insert("test1", SessionValue::Null);
644        sd.insert("TEst2", SessionValue::Bool(true));
645        sd.insert("tesT3", SessionValue::Integer(1));
646
647        let mut test1_seen = false;
648        let mut test2_seen = false;
649        let mut test3_seen = false;
650        for key in sd.keys() {
651            match key.as_str() {
652                "test1" => {
653                    assert!(!test1_seen);
654                    test1_seen = true;
655                }
656                "test2" => {
657                    assert!(!test2_seen);
658                    test2_seen = true;
659                }
660                "test3" => {
661                    assert!(!test3_seen);
662                    test3_seen = true;
663                }
664                _ => panic!("Unexpected key: {key}"),
665            }
666        }
667        assert!(test1_seen);
668        assert!(test2_seen);
669        assert!(test3_seen);
670
671        test1_seen = false;
672        test2_seen = false;
673        test3_seen = false;
674        for key in sd.into_keys() {
675            match key.as_str() {
676                "test1" => {
677                    assert!(!test1_seen);
678                    test1_seen = true;
679                }
680                "test2" => {
681                    assert!(!test2_seen);
682                    test2_seen = true;
683                }
684                "test3" => {
685                    assert!(!test3_seen);
686                    test3_seen = true;
687                }
688                _ => panic!("Unexpected key: {key}"),
689            }
690        }
691        assert!(test1_seen);
692        assert!(test2_seen);
693        assert!(test3_seen);
694    }
695
696    #[test]
697    #[allow(clippy::manual_range_contains)]
698    fn check_values() {
699        let mut sd: SessionData = Default::default();
700        sd.try_reserve(3).unwrap();
701        sd.insert("test0", SessionValue::Null);
702        sd.insert("TEst1", SessionValue::from(true));
703        sd.insert("tesT2", SessionValue::from(1));
704        sd.insert("tESt3", SessionValue::from(Ipv4Addr::new(192, 0, 2, 1)));
705        sd.insert("tESt4", SessionValue::from(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)));
706        sd.insert("tESt5", SessionValue::from(IpAddr::V4(Ipv4Addr::new(192, 0, 2, 2))));
707        sd.insert("tESt6", SessionValue::from("Hello World"));
708        sd.insert(
709            "test7",
710            SessionValue::from(DateTime::<Utc>::from_utc(
711                NaiveDate::from_ymd_opt(2019, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(),
712                Utc,
713            )),
714        );
715        sd.insert("test8", SessionValue::Binary(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
716        sd.insert("test9", SessionValue::from(false));
717        sd.insert("test10", SessionValue::from("Hello World2".to_string()));
718        sd.insert(
719            "test11",
720            SessionValue::from(DateTime::<FixedOffset>::from_local(
721                NaiveDate::from_ymd_opt(2019, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(),
722                FixedOffset::west_opt(8 * 3600).unwrap(),
723            )),
724        );
725
726        let mut test_seen = [false; 12];
727        for value in sd.values_mut() {
728            match value {
729                SessionValue::Null => {
730                    assert!(!test_seen[0]);
731                    assert!(value.is_null());
732                    assert_eq!(value.as_variable_value(), "");
733                    test_seen[0] = true;
734                    *value = SessionValue::Integer(100);
735                }
736                SessionValue::Bool(true) => {
737                    assert!(!value.is_null());
738                    assert!(!test_seen[1]);
739                    assert_eq!(value.as_variable_value(), "true");
740                    test_seen[1] = true;
741                    *value = SessionValue::Integer(101);
742                }
743                SessionValue::Integer(1) => {
744                    assert!(!value.is_null());
745                    assert!(!test_seen[2]);
746                    assert_eq!(value.as_variable_value(), "1");
747                    test_seen[2] = true;
748                    *value = SessionValue::Integer(102);
749                }
750                SessionValue::IpAddr(IpAddr::V4(v4)) if v4 == &Ipv4Addr::new(192, 0, 2, 1) => {
751                    assert!(!value.is_null());
752                    assert!(!test_seen[3]);
753                    assert_eq!(value.as_variable_value(), "192.0.2.1");
754                    test_seen[3] = true;
755                    *value = SessionValue::Integer(103);
756                }
757                SessionValue::IpAddr(IpAddr::V6(v6)) if v6 == &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1) => {
758                    assert!(!value.is_null());
759                    assert!(!test_seen[4]);
760                    assert_eq!(value.as_variable_value(), "2001:db8::1");
761                    test_seen[4] = true;
762                    *value = SessionValue::Integer(104);
763                }
764                SessionValue::IpAddr(IpAddr::V4(v4)) if v4 == &Ipv4Addr::new(192, 0, 2, 2) => {
765                    assert!(!value.is_null());
766                    assert!(!test_seen[5]);
767                    assert_eq!(value.as_variable_value(), "192.0.2.2");
768                    test_seen[5] = true;
769                    *value = SessionValue::Integer(105);
770                }
771                SessionValue::String(s) if s == "Hello World" => {
772                    assert!(!value.is_null());
773                    assert!(!test_seen[6]);
774                    assert_eq!(value.as_variable_value(), "Hello World");
775                    test_seen[6] = true;
776                    *value = SessionValue::Integer(106);
777                }
778                SessionValue::Timestamp(dt)
779                    if dt
780                        == &DateTime::<Utc>::from_utc(
781                            NaiveDate::from_ymd_opt(2019, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(),
782                            Utc,
783                        ) =>
784                {
785                    assert!(!value.is_null());
786                    assert!(!test_seen[7]);
787                    assert_eq!(value.as_variable_value(), "2019-01-01T00:00:00Z");
788                    assert_eq!(format!("{value}"), "2019-01-01T00:00:00Z");
789                    test_seen[7] = true;
790                    *value = SessionValue::Integer(107);
791                }
792                SessionValue::Binary(v) => {
793                    assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
794                    assert!(!value.is_null());
795                    assert!(!test_seen[8]);
796                    assert_eq!(value.as_variable_value(), "AAECAwQFBgcICQ==");
797                    assert_eq!(format!("{value}"), "AAECAwQFBgcICQ==");
798                    test_seen[8] = true;
799                    *value = SessionValue::Integer(108);
800                }
801                SessionValue::Bool(false) => {
802                    assert!(!value.is_null());
803                    assert!(!test_seen[9]);
804                    assert_eq!(value.as_variable_value(), "false");
805                    test_seen[9] = true;
806                    *value = SessionValue::Integer(109);
807                }
808                SessionValue::String(s) if s == "Hello World2" => {
809                    assert!(!value.is_null());
810                    assert!(!test_seen[10]);
811                    assert_eq!(value.as_variable_value(), "Hello World2");
812                    test_seen[10] = true;
813                    *value = SessionValue::Integer(110);
814                }
815                SessionValue::Timestamp(dt)
816                    if dt
817                        == &DateTime::<Utc>::from_utc(
818                            NaiveDate::from_ymd_opt(2019, 1, 1).unwrap().and_hms_opt(8, 0, 0).unwrap(),
819                            Utc,
820                        ) =>
821                {
822                    assert!(!value.is_null());
823                    assert!(!test_seen[11]);
824                    assert_eq!(value.as_variable_value(), "2019-01-01T08:00:00Z");
825                    assert_eq!(format!("{value}"), "2019-01-01T08:00:00Z");
826                    test_seen[11] = true;
827                    *value = SessionValue::Integer(111);
828                }
829                _ => panic!("Unexpected value: {value}"),
830            }
831        }
832
833        assert!(test_seen.iter().all(|&v| v));
834        test_seen.iter_mut().for_each(|v| *v = false);
835
836        let test_range = 100i64..(100i64 + test_seen.len() as i64);
837
838        for value in sd.values() {
839            match value {
840                SessionValue::Integer(i) if test_range.contains(i) => {
841                    let i = (i - 100) as usize;
842                    assert!(!test_seen[i]);
843                    test_seen[i] = true;
844                }
845                _ => panic!("Unexpected value: {value}"),
846            }
847        }
848
849        assert!(test_seen.iter().all(|&v| v));
850        test_seen.iter_mut().for_each(|v| *v = false);
851        for value in sd.into_values() {
852            match value {
853                SessionValue::Integer(i) if test_range.contains(&i) => {
854                    let i = (i - 100) as usize;
855                    assert!(!test_seen[i]);
856                    test_seen[i] = true;
857                }
858                _ => panic!("Unexpected value: {value}"),
859            }
860        }
861        assert!(test_seen.iter().all(|&v| v));
862    }
863
864    #[test]
865    fn check_iter() {
866        let mut sd: SessionData = Default::default();
867        sd.try_reserve(3).unwrap();
868        sd.insert("test1", SessionValue::Null);
869        sd.insert("TEst2", SessionValue::Bool(true));
870        sd.insert("tesT3", SessionValue::Integer(1));
871
872        let mut test1_seen = false;
873        let mut test2_seen = false;
874        let mut test3_seen = false;
875        for (key, value) in sd.iter_mut() {
876            match key.as_str() {
877                "test1" => {
878                    assert_eq!(value, &SessionValue::Null);
879                    assert!(!test1_seen);
880                    *value = SessionValue::Integer(100);
881                    test1_seen = true;
882                }
883                "test2" => {
884                    assert_eq!(value, &SessionValue::Bool(true));
885                    assert!(!test2_seen);
886                    *value = SessionValue::Integer(101);
887                    test2_seen = true;
888                }
889                "test3" => {
890                    assert_eq!(value, &SessionValue::Integer(1));
891                    assert!(!test3_seen);
892                    *value = SessionValue::Integer(102);
893                    test3_seen = true;
894                }
895                _ => panic!("Unexpected value: {value}"),
896            }
897        }
898        assert!(test1_seen);
899        assert!(test2_seen);
900        assert!(test3_seen);
901
902        let mut test1_seen = false;
903        let mut test2_seen = false;
904        let mut test3_seen = false;
905        for (key, value) in sd.iter() {
906            match key.as_str() {
907                "test1" => {
908                    assert_eq!(value, &SessionValue::Integer(100));
909                    assert!(!test1_seen);
910                    test1_seen = true;
911                }
912                "test2" => {
913                    assert_eq!(value, &SessionValue::Integer(101));
914                    assert!(!test2_seen);
915                    test2_seen = true;
916                }
917                "test3" => {
918                    assert_eq!(value, &SessionValue::Integer(102));
919                    assert!(!test3_seen);
920                    test3_seen = true;
921                }
922                _ => panic!("Unexpected key: {key}"),
923            }
924        }
925        assert!(test1_seen);
926        assert!(test2_seen);
927        assert!(test3_seen);
928
929        let mut sd_mut = sd.clone();
930
931        test1_seen = false;
932        test2_seen = false;
933        test3_seen = false;
934        for (key, value) in (&sd).into_iter() {
935            match key.as_str() {
936                "test1" => {
937                    assert_eq!(value, &SessionValue::Integer(100));
938                    assert!(!test1_seen);
939                    test1_seen = true;
940                }
941                "test2" => {
942                    assert_eq!(value, &SessionValue::Integer(101));
943                    assert!(!test2_seen);
944                    test2_seen = true;
945                }
946                "test3" => {
947                    assert_eq!(value, &SessionValue::Integer(102));
948                    assert!(!test3_seen);
949                    test3_seen = true;
950                }
951                _ => panic!("Unexpected key: {key}"),
952            }
953        }
954        assert!(test1_seen);
955        assert!(test2_seen);
956        assert!(test3_seen);
957
958        test1_seen = false;
959        test2_seen = false;
960        test3_seen = false;
961        for (key, value) in sd.into_iter() {
962            match key.as_str() {
963                "test1" => {
964                    assert_eq!(value, SessionValue::Integer(100));
965                    assert!(!test1_seen);
966                    test1_seen = true;
967                }
968                "test2" => {
969                    assert_eq!(value, SessionValue::Integer(101));
970                    assert!(!test2_seen);
971                    test2_seen = true;
972                }
973                "test3" => {
974                    assert_eq!(value, SessionValue::Integer(102));
975                    assert!(!test3_seen);
976                    test3_seen = true;
977                }
978                _ => panic!("Unexpected key: {key}"),
979            }
980        }
981        assert!(test1_seen);
982        assert!(test2_seen);
983        assert!(test3_seen);
984
985        sd_mut.retain(|k, _| k != "test3");
986
987        test1_seen = false;
988        test2_seen = false;
989        test3_seen = false;
990        for (key, value) in (&mut sd_mut).into_iter() {
991            match key.as_str() {
992                "test1" => {
993                    assert_eq!(value, &SessionValue::Integer(100));
994                    assert!(!test1_seen);
995                    test1_seen = true;
996                }
997                "test2" => {
998                    assert_eq!(value, &SessionValue::Integer(101));
999                    assert!(!test2_seen);
1000                    test2_seen = true;
1001                }
1002                _ => panic!("Unexpected key: {key}"),
1003            }
1004        }
1005        assert!(test1_seen);
1006        assert!(test2_seen);
1007        assert!(!test3_seen);
1008    }
1009}
1010// end tests -- do not delete; needed for coverage.