use {
chrono::{DateTime, FixedOffset, Utc},
std::{
collections::{
hash_map::{Drain, Entry, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, ValuesMut},
HashMap, TryReserveError,
},
fmt::{Display, Formatter, Result as FmtResult},
hash::Hash,
iter::{Extend, FromIterator, IntoIterator},
net::{IpAddr, Ipv4Addr, Ipv6Addr},
ops::Index,
panic::UnwindSafe,
},
};
#[derive(Clone, Debug)]
pub struct SessionData {
variables: HashMap<String, SessionValue>,
}
impl SessionData {
pub fn new() -> Self {
Self {
variables: HashMap::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
variables: HashMap::with_capacity(capacity),
}
}
pub fn capacity(&self) -> usize {
self.variables.capacity()
}
pub fn clear(&mut self) {
self.variables.clear();
}
pub fn contains_key<Q: AsRef<str> + ?Sized>(&self, k: &Q) -> bool {
self.variables.contains_key(&k.as_ref().to_lowercase())
}
pub fn drain(&mut self) -> Drain<'_, String, SessionValue> {
self.variables.drain()
}
pub fn entry<Q: AsRef<str> + ?Sized>(&mut self, key: &Q) -> Entry<'_, String, SessionValue> {
self.variables.entry(key.as_ref().to_lowercase())
}
pub fn get<Q: AsRef<str> + ?Sized>(&self, key: &Q) -> Option<&SessionValue> {
self.variables.get(&key.as_ref().to_lowercase())
}
pub fn get_key_value<Q: AsRef<str> + ?Sized>(&self, key: &Q) -> Option<(&str, &SessionValue)> {
self.variables.get_key_value(&key.as_ref().to_lowercase()).map(|(key, value)| (key.as_str(), value))
}
pub fn get_mut<Q: AsRef<str> + ?Sized>(&mut self, key: &Q) -> Option<&mut SessionValue> {
self.variables.get_mut(&key.as_ref().to_lowercase())
}
pub fn insert<Q: AsRef<str> + ?Sized>(&mut self, key: &Q, value: SessionValue) -> Option<SessionValue> {
self.variables.insert(key.as_ref().to_lowercase(), value)
}
pub fn into_keys(self) -> IntoKeys<String, SessionValue> {
self.variables.into_keys()
}
pub fn into_values(self) -> IntoValues<String, SessionValue> {
self.variables.into_values()
}
pub fn iter(&self) -> Iter<'_, String, SessionValue> {
self.variables.iter()
}
pub fn iter_mut(&mut self) -> IterMut<'_, String, SessionValue> {
self.variables.iter_mut()
}
pub fn is_empty(&self) -> bool {
self.variables.is_empty()
}
pub fn keys(&self) -> Keys<'_, String, SessionValue> {
self.variables.keys()
}
pub fn len(&self) -> usize {
self.variables.len()
}
pub fn remove<Q: AsRef<str> + ?Sized>(&mut self, key: &Q) -> Option<SessionValue> {
self.variables.remove(&key.as_ref().to_lowercase())
}
pub fn remove_entry<Q: AsRef<str> + ?Sized>(&mut self, key: &Q) -> Option<(String, SessionValue)> {
self.variables.remove_entry(&key.as_ref().to_lowercase())
}
pub fn reserve(&mut self, additional: usize) {
self.variables.reserve(additional)
}
pub fn retain<F: FnMut(&str, &mut SessionValue) -> bool>(&mut self, mut f: F) {
self.variables.retain(|key, value| f(key.as_str(), value))
}
pub fn shrink_to(&mut self, min_capacity: usize) {
self.variables.shrink_to(min_capacity)
}
pub fn shrink_to_fit(&mut self) {
self.variables.shrink_to_fit()
}
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.variables.try_reserve(additional)
}
pub fn values(&self) -> Values<'_, String, SessionValue> {
self.variables.values()
}
pub fn values_mut(&mut self) -> ValuesMut<'_, String, SessionValue> {
self.variables.values_mut()
}
}
impl Default for SessionData {
fn default() -> Self {
Self::new()
}
}
impl<'a, K: AsRef<str> + ?Sized> Extend<(&'a K, &'a SessionValue)> for SessionData {
fn extend<T: IntoIterator<Item = (&'a K, &'a SessionValue)>>(&mut self, iter: T) {
self.variables.extend(iter.into_iter().map(|(key, value)| (key.as_ref().to_lowercase(), value.clone())));
}
}
impl From<HashMap<String, SessionValue>> for SessionData {
fn from(variables: HashMap<String, SessionValue>) -> Self {
let mut my_vars = HashMap::new();
for (key, value) in variables.iter() {
my_vars.insert(key.to_lowercase(), value.clone());
}
Self {
variables: my_vars,
}
}
}
impl<K: AsRef<str>, const N: usize> From<[(K, SessionValue); N]> for SessionData {
fn from(variables: [(K, SessionValue); N]) -> Self {
let mut my_vars = HashMap::new();
for (key, value) in variables.iter() {
my_vars.insert(key.as_ref().to_lowercase(), value.clone());
}
Self {
variables: my_vars,
}
}
}
impl<K: AsRef<str>> FromIterator<(K, SessionValue)> for SessionData {
fn from_iter<T: IntoIterator<Item = (K, SessionValue)>>(iter: T) -> Self {
let mut my_vars = HashMap::new();
for (key, value) in iter {
my_vars.insert(key.as_ref().to_lowercase(), value.clone());
}
Self {
variables: my_vars,
}
}
}
impl<Q: AsRef<str> + ?Sized> Index<&'_ Q> for SessionData {
type Output = SessionValue;
fn index(&self, key: &Q) -> &Self::Output {
self.variables.get(&key.as_ref().to_lowercase()).unwrap()
}
}
impl<'a> IntoIterator for &'a SessionData {
type Item = (&'a String, &'a SessionValue);
type IntoIter = Iter<'a, String, SessionValue>;
fn into_iter(self) -> Self::IntoIter {
self.variables.iter()
}
}
impl<'a> IntoIterator for &'a mut SessionData {
type Item = (&'a String, &'a mut SessionValue);
type IntoIter = IterMut<'a, String, SessionValue>;
fn into_iter(self) -> Self::IntoIter {
self.variables.iter_mut()
}
}
impl IntoIterator for SessionData {
type Item = (String, SessionValue);
type IntoIter = IntoIter<String, SessionValue>;
fn into_iter(self) -> Self::IntoIter {
self.variables.into_iter()
}
}
impl UnwindSafe for SessionData {}
impl PartialEq for SessionData {
fn eq(&self, other: &Self) -> bool {
self.variables == other.variables
}
}
impl PartialEq<HashMap<String, SessionValue>> for SessionData {
fn eq(&self, other: &HashMap<String, SessionValue>) -> bool {
if self.variables.len() != other.len() {
return false;
}
for (key, other_value) in other.iter() {
match self.variables.get(&key.to_lowercase()) {
None => return false,
Some(value) => {
if value != other_value {
return false;
}
}
}
}
true
}
}
impl Eq for SessionData {}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SessionValue {
Null,
Binary(Vec<u8>),
Bool(bool),
Integer(i64),
IpAddr(IpAddr),
String(String),
Timestamp(DateTime<Utc>),
}
impl SessionValue {
#[inline]
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn as_variable_value(&self) -> String {
match self {
Self::Null => "".to_string(),
Self::Binary(value) => base64::encode(value),
Self::Bool(b) => if *b {
"true"
} else {
"false"
}
.to_string(),
Self::Integer(i) => format!("{i}"),
Self::IpAddr(ip) => format!("{ip}"),
Self::String(s) => s.clone(),
Self::Timestamp(t) => format!("{}", t.format("%Y-%m-%dT%H:%M:%SZ")),
}
}
}
impl From<bool> for SessionValue {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
impl From<i64> for SessionValue {
fn from(value: i64) -> Self {
Self::Integer(value)
}
}
impl From<IpAddr> for SessionValue {
fn from(value: IpAddr) -> Self {
Self::IpAddr(value)
}
}
impl From<Ipv4Addr> for SessionValue {
fn from(value: Ipv4Addr) -> Self {
Self::IpAddr(IpAddr::V4(value))
}
}
impl From<Ipv6Addr> for SessionValue {
fn from(value: Ipv6Addr) -> Self {
Self::IpAddr(IpAddr::V6(value))
}
}
impl From<&str> for SessionValue {
fn from(value: &str) -> Self {
Self::String(value.to_string())
}
}
impl From<String> for SessionValue {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<DateTime<FixedOffset>> for SessionValue {
fn from(value: DateTime<FixedOffset>) -> Self {
Self::Timestamp(value.into())
}
}
impl From<DateTime<Utc>> for SessionValue {
fn from(value: DateTime<Utc>) -> Self {
Self::Timestamp(value)
}
}
impl Display for SessionValue {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Null => f.write_str("null"),
Self::Binary(value) => f.write_str(&base64::encode(value)),
Self::Bool(b) => Display::fmt(b, f),
Self::Integer(i) => Display::fmt(i, f),
Self::IpAddr(ip) => Display::fmt(ip, f),
Self::String(s) => f.write_str(s),
Self::Timestamp(t) => write!(f, "{}", t.format("%Y-%m-%dT%H:%M:%SZ")),
}
}
}
#[cfg(test)]
mod tests {
use {
super::{SessionData, SessionValue},
chrono::{DateTime, FixedOffset, NaiveDate, Utc},
std::{
cmp::Ordering,
collections::hash_map::{DefaultHasher, HashMap},
hash::{Hash, Hasher},
iter::IntoIterator,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
},
};
#[test]
fn check_session_value_derived() {
let sv1a = SessionValue::Null;
let sv1b = SessionValue::Null;
let sv2 = SessionValue::Bool(true);
let values = vec![
SessionValue::Null,
SessionValue::Bool(false),
SessionValue::Bool(true),
SessionValue::Integer(-1),
SessionValue::Integer(0),
SessionValue::Integer(1),
SessionValue::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
SessionValue::IpAddr(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
SessionValue::String("test1".to_string()),
SessionValue::String("test2".to_string()),
];
let display = vec!["null", "false", "true", "-1", "0", "1", "127.0.0.1", "::1", "test1", "test2"];
assert_eq!(sv1a, sv1b);
assert_ne!(sv1a, sv2);
assert_eq!(sv1a, sv1a.clone());
let mut h1a = DefaultHasher::new();
let mut h1b = DefaultHasher::new();
let mut h2 = DefaultHasher::new();
sv1a.hash(&mut h1a);
sv1b.hash(&mut h1b);
sv2.hash(&mut h2);
let hash1a = h1a.finish();
let hash1b = h1b.finish();
let hash2 = h2.finish();
assert_eq!(hash1a, hash1b);
assert_ne!(hash1a, hash2);
for i in 0..values.len() {
for j in 0..values.len() {
assert_eq!(values[i].cmp(&values[j]), i.cmp(&j));
match i.cmp(&j) {
Ordering::Less => assert!(values[i] < values[j]),
Ordering::Equal => assert!(values[i] == values[j]),
Ordering::Greater => assert!(values[i] > values[j]),
}
}
}
for ref value in values.iter() {
let _ = format!("{value:?}");
}
for i in 0..values.len() {
assert_eq!(values[i].to_string(), display[i]);
}
}
#[test]
fn check_case_sensitivity() {
let mut sd = SessionData::new();
assert!(sd.is_empty());
sd.insert("test", SessionValue::Null);
assert!(sd.contains_key("TEST"));
assert!(sd.contains_key("test"));
sd.insert("Test", SessionValue::Bool(true));
sd.insert("TeST2", SessionValue::Integer(100));
assert!(sd.contains_key("tEsT"));
assert!(sd.contains_key("test"));
assert_eq!(sd.len(), 2);
assert!(!sd.is_empty());
assert_eq!(sd.get("test"), Some(&SessionValue::Bool(true)));
assert!(sd.contains_key("tEsT"));
assert!(sd.contains_key("test"));
assert_eq!(sd.get_key_value("tEST"), Some(("test", &SessionValue::Bool(true))));
let sv = sd.get_mut("tesT");
assert!(sv.is_some());
*sv.unwrap() = SessionValue::String("Hello".to_string());
assert_eq!(sd.get("test"), Some(&SessionValue::String("Hello".to_string())));
sd.entry("test2").and_modify(|v| *v = SessionValue::Integer(200));
assert_eq!(sd["test2"], SessionValue::Integer(200));
assert!(sd.remove("teST").is_some());
assert!(sd.remove("test").is_none());
assert!(sd.remove_entry("test2").is_some());
}
#[test]
fn check_clone_eq() {
let mut sd1 = SessionData::new();
sd1.insert("test", SessionValue::String("Hello World".to_string()));
let sd2 = sd1.clone();
assert_eq!(sd1, sd2);
let mut sd3 = SessionData::with_capacity(1);
assert!(sd3.capacity() > 0);
sd3.insert("TEST", SessionValue::String("Hello World".to_string()));
assert_eq!(sd1, sd3);
sd3.drain();
assert_ne!(sd1, sd3);
sd3.insert("test", SessionValue::String("Hello again".to_string()));
assert_ne!(sd1, sd3);
sd3.insert("test", SessionValue::Integer(1));
assert_ne!(sd1, sd3);
sd3.clear();
sd3.insert("TeST", SessionValue::String("Hello World".to_string()));
assert_eq!(sd1, sd3);
let mut h = HashMap::with_capacity(1);
h.insert("test".to_string(), SessionValue::String("Hello World".to_string()));
assert_eq!(sd1, h);
h.drain();
assert_ne!(sd1, h);
h.insert("test".to_string(), SessionValue::String("Hello again".to_string()));
assert_ne!(sd1, h);
h.insert("test".to_string(), SessionValue::Integer(1));
assert_ne!(sd1, h);
h.insert("test".to_string(), SessionValue::String("Hello World".to_string()));
assert_eq!(sd1, h);
h.drain();
h.insert("test2".to_string(), SessionValue::String("Hello World".to_string()));
assert_ne!(sd1, h);
sd1.clear();
sd1.shrink_to_fit();
assert_eq!(sd1.capacity(), 0);
sd1.reserve(100);
assert!(sd1.capacity() >= 100);
sd1.shrink_to(50);
assert!(sd1.capacity() >= 50);
let _ = format!("{sd1:?}");
}
#[test]
fn check_from_hashmap() {
let mut h = HashMap::new();
h.insert("Test".to_string(), SessionValue::String("Hello World".to_string()));
let mut sd = SessionData::from(h);
assert_eq!(sd.len(), 1);
assert_eq!(sd["test"], SessionValue::String("Hello World".to_string()));
let to_add = vec![("Test2", SessionValue::Integer(100)), ("Test3", SessionValue::Bool(true))];
sd.extend(to_add.iter().map(|r| (r.0, &r.1)));
assert_eq!(sd.len(), 3);
assert_eq!(sd["test2"], SessionValue::Integer(100));
assert_eq!(sd["test3"], SessionValue::Bool(true));
}
#[test]
fn check_from_array() {
let values: [(String, SessionValue); 3] = [
("Test".to_string(), SessionValue::String("Hello World".to_string())),
("Test2".to_string(), SessionValue::Integer(100)),
("Test3".to_string(), SessionValue::Bool(true)),
];
let sd = SessionData::from(values);
assert_eq!(sd.len(), 3);
assert_eq!(sd["test"], SessionValue::String("Hello World".to_string()));
assert_eq!(sd["test2"], SessionValue::Integer(100));
assert_eq!(sd["test3"], SessionValue::Bool(true));
}
#[test]
fn check_from_iter() {
let values = vec![
("Test".to_string(), SessionValue::String("Hello World".to_string())),
("Test2".to_string(), SessionValue::Integer(100)),
("Test3".to_string(), SessionValue::Bool(true)),
];
let sd = SessionData::from_iter(values);
assert_eq!(sd.len(), 3);
assert_eq!(sd["test"], SessionValue::String("Hello World".to_string()));
assert_eq!(sd["test2"], SessionValue::Integer(100));
assert_eq!(sd["test3"], SessionValue::Bool(true));
}
#[test]
fn check_keys() {
let mut sd: SessionData = Default::default();
sd.try_reserve(3).unwrap();
sd.insert("test1", SessionValue::Null);
sd.insert("TEst2", SessionValue::Bool(true));
sd.insert("tesT3", SessionValue::Integer(1));
let mut test1_seen = false;
let mut test2_seen = false;
let mut test3_seen = false;
for key in sd.keys() {
match key.as_str() {
"test1" => {
assert!(!test1_seen);
test1_seen = true;
}
"test2" => {
assert!(!test2_seen);
test2_seen = true;
}
"test3" => {
assert!(!test3_seen);
test3_seen = true;
}
_ => panic!("Unexpected key: {key}"),
}
}
assert!(test1_seen);
assert!(test2_seen);
assert!(test3_seen);
test1_seen = false;
test2_seen = false;
test3_seen = false;
for key in sd.into_keys() {
match key.as_str() {
"test1" => {
assert!(!test1_seen);
test1_seen = true;
}
"test2" => {
assert!(!test2_seen);
test2_seen = true;
}
"test3" => {
assert!(!test3_seen);
test3_seen = true;
}
_ => panic!("Unexpected key: {key}"),
}
}
assert!(test1_seen);
assert!(test2_seen);
assert!(test3_seen);
}
#[test]
#[allow(clippy::manual_range_contains)]
fn check_values() {
let mut sd: SessionData = Default::default();
sd.try_reserve(3).unwrap();
sd.insert("test0", SessionValue::Null);
sd.insert("TEst1", SessionValue::from(true));
sd.insert("tesT2", SessionValue::from(1));
sd.insert("tESt3", SessionValue::from(Ipv4Addr::new(192, 0, 2, 1)));
sd.insert("tESt4", SessionValue::from(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)));
sd.insert("tESt5", SessionValue::from(IpAddr::V4(Ipv4Addr::new(192, 0, 2, 2))));
sd.insert("tESt6", SessionValue::from("Hello World"));
sd.insert(
"test7",
SessionValue::from(DateTime::<Utc>::from_utc(
NaiveDate::from_ymd_opt(2019, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(),
Utc,
)),
);
sd.insert("test8", SessionValue::Binary(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
sd.insert("test9", SessionValue::from(false));
sd.insert("test10", SessionValue::from("Hello World2".to_string()));
sd.insert(
"test11",
SessionValue::from(DateTime::<FixedOffset>::from_local(
NaiveDate::from_ymd_opt(2019, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(),
FixedOffset::west_opt(8 * 3600).unwrap(),
)),
);
let mut test_seen = [false; 12];
for value in sd.values_mut() {
match value {
SessionValue::Null => {
assert!(!test_seen[0]);
assert!(value.is_null());
assert_eq!(value.as_variable_value(), "");
test_seen[0] = true;
*value = SessionValue::Integer(100);
}
SessionValue::Bool(true) => {
assert!(!value.is_null());
assert!(!test_seen[1]);
assert_eq!(value.as_variable_value(), "true");
test_seen[1] = true;
*value = SessionValue::Integer(101);
}
SessionValue::Integer(1) => {
assert!(!value.is_null());
assert!(!test_seen[2]);
assert_eq!(value.as_variable_value(), "1");
test_seen[2] = true;
*value = SessionValue::Integer(102);
}
SessionValue::IpAddr(IpAddr::V4(v4)) if v4 == &Ipv4Addr::new(192, 0, 2, 1) => {
assert!(!value.is_null());
assert!(!test_seen[3]);
assert_eq!(value.as_variable_value(), "192.0.2.1");
test_seen[3] = true;
*value = SessionValue::Integer(103);
}
SessionValue::IpAddr(IpAddr::V6(v6)) if v6 == &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1) => {
assert!(!value.is_null());
assert!(!test_seen[4]);
assert_eq!(value.as_variable_value(), "2001:db8::1");
test_seen[4] = true;
*value = SessionValue::Integer(104);
}
SessionValue::IpAddr(IpAddr::V4(v4)) if v4 == &Ipv4Addr::new(192, 0, 2, 2) => {
assert!(!value.is_null());
assert!(!test_seen[5]);
assert_eq!(value.as_variable_value(), "192.0.2.2");
test_seen[5] = true;
*value = SessionValue::Integer(105);
}
SessionValue::String(s) if s == "Hello World" => {
assert!(!value.is_null());
assert!(!test_seen[6]);
assert_eq!(value.as_variable_value(), "Hello World");
test_seen[6] = true;
*value = SessionValue::Integer(106);
}
SessionValue::Timestamp(dt)
if dt
== &DateTime::<Utc>::from_utc(
NaiveDate::from_ymd_opt(2019, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(),
Utc,
) =>
{
assert!(!value.is_null());
assert!(!test_seen[7]);
assert_eq!(value.as_variable_value(), "2019-01-01T00:00:00Z");
assert_eq!(format!("{value}"), "2019-01-01T00:00:00Z");
test_seen[7] = true;
*value = SessionValue::Integer(107);
}
SessionValue::Binary(v) => {
assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert!(!value.is_null());
assert!(!test_seen[8]);
assert_eq!(value.as_variable_value(), "AAECAwQFBgcICQ==");
assert_eq!(format!("{value}"), "AAECAwQFBgcICQ==");
test_seen[8] = true;
*value = SessionValue::Integer(108);
}
SessionValue::Bool(false) => {
assert!(!value.is_null());
assert!(!test_seen[9]);
assert_eq!(value.as_variable_value(), "false");
test_seen[9] = true;
*value = SessionValue::Integer(109);
}
SessionValue::String(s) if s == "Hello World2" => {
assert!(!value.is_null());
assert!(!test_seen[10]);
assert_eq!(value.as_variable_value(), "Hello World2");
test_seen[10] = true;
*value = SessionValue::Integer(110);
}
SessionValue::Timestamp(dt)
if dt
== &DateTime::<Utc>::from_utc(
NaiveDate::from_ymd_opt(2019, 1, 1).unwrap().and_hms_opt(8, 0, 0).unwrap(),
Utc,
) =>
{
assert!(!value.is_null());
assert!(!test_seen[11]);
assert_eq!(value.as_variable_value(), "2019-01-01T08:00:00Z");
assert_eq!(format!("{value}"), "2019-01-01T08:00:00Z");
test_seen[11] = true;
*value = SessionValue::Integer(111);
}
_ => panic!("Unexpected value: {value}"),
}
}
assert!(test_seen.iter().all(|&v| v));
test_seen.iter_mut().for_each(|v| *v = false);
let test_range = 100i64..(100i64 + test_seen.len() as i64);
for value in sd.values() {
match value {
SessionValue::Integer(i) if test_range.contains(i) => {
let i = (i - 100) as usize;
assert!(!test_seen[i]);
test_seen[i] = true;
}
_ => panic!("Unexpected value: {value}"),
}
}
assert!(test_seen.iter().all(|&v| v));
test_seen.iter_mut().for_each(|v| *v = false);
for value in sd.into_values() {
match value {
SessionValue::Integer(i) if test_range.contains(&i) => {
let i = (i - 100) as usize;
assert!(!test_seen[i]);
test_seen[i] = true;
}
_ => panic!("Unexpected value: {value}"),
}
}
assert!(test_seen.iter().all(|&v| v));
}
#[test]
fn check_iter() {
let mut sd: SessionData = Default::default();
sd.try_reserve(3).unwrap();
sd.insert("test1", SessionValue::Null);
sd.insert("TEst2", SessionValue::Bool(true));
sd.insert("tesT3", SessionValue::Integer(1));
let mut test1_seen = false;
let mut test2_seen = false;
let mut test3_seen = false;
for (key, value) in sd.iter_mut() {
match key.as_str() {
"test1" => {
assert_eq!(value, &SessionValue::Null);
assert!(!test1_seen);
*value = SessionValue::Integer(100);
test1_seen = true;
}
"test2" => {
assert_eq!(value, &SessionValue::Bool(true));
assert!(!test2_seen);
*value = SessionValue::Integer(101);
test2_seen = true;
}
"test3" => {
assert_eq!(value, &SessionValue::Integer(1));
assert!(!test3_seen);
*value = SessionValue::Integer(102);
test3_seen = true;
}
_ => panic!("Unexpected value: {value}"),
}
}
assert!(test1_seen);
assert!(test2_seen);
assert!(test3_seen);
let mut test1_seen = false;
let mut test2_seen = false;
let mut test3_seen = false;
for (key, value) in sd.iter() {
match key.as_str() {
"test1" => {
assert_eq!(value, &SessionValue::Integer(100));
assert!(!test1_seen);
test1_seen = true;
}
"test2" => {
assert_eq!(value, &SessionValue::Integer(101));
assert!(!test2_seen);
test2_seen = true;
}
"test3" => {
assert_eq!(value, &SessionValue::Integer(102));
assert!(!test3_seen);
test3_seen = true;
}
_ => panic!("Unexpected key: {key}"),
}
}
assert!(test1_seen);
assert!(test2_seen);
assert!(test3_seen);
let mut sd_mut = sd.clone();
test1_seen = false;
test2_seen = false;
test3_seen = false;
for (key, value) in (&sd).into_iter() {
match key.as_str() {
"test1" => {
assert_eq!(value, &SessionValue::Integer(100));
assert!(!test1_seen);
test1_seen = true;
}
"test2" => {
assert_eq!(value, &SessionValue::Integer(101));
assert!(!test2_seen);
test2_seen = true;
}
"test3" => {
assert_eq!(value, &SessionValue::Integer(102));
assert!(!test3_seen);
test3_seen = true;
}
_ => panic!("Unexpected key: {key}"),
}
}
assert!(test1_seen);
assert!(test2_seen);
assert!(test3_seen);
test1_seen = false;
test2_seen = false;
test3_seen = false;
for (key, value) in sd.into_iter() {
match key.as_str() {
"test1" => {
assert_eq!(value, SessionValue::Integer(100));
assert!(!test1_seen);
test1_seen = true;
}
"test2" => {
assert_eq!(value, SessionValue::Integer(101));
assert!(!test2_seen);
test2_seen = true;
}
"test3" => {
assert_eq!(value, SessionValue::Integer(102));
assert!(!test3_seen);
test3_seen = true;
}
_ => panic!("Unexpected key: {key}"),
}
}
assert!(test1_seen);
assert!(test2_seen);
assert!(test3_seen);
sd_mut.retain(|k, _| k != "test3");
test1_seen = false;
test2_seen = false;
test3_seen = false;
for (key, value) in (&mut sd_mut).into_iter() {
match key.as_str() {
"test1" => {
assert_eq!(value, &SessionValue::Integer(100));
assert!(!test1_seen);
test1_seen = true;
}
"test2" => {
assert_eq!(value, &SessionValue::Integer(101));
assert!(!test2_seen);
test2_seen = true;
}
_ => panic!("Unexpected key: {key}"),
}
}
assert!(test1_seen);
assert!(test2_seen);
assert!(!test3_seen);
}
}