use std::cmp::Ordering;
use std::collections::BTreeMap;
pub(crate) trait TreeMapExtension<K, V>
where
K: std::cmp::Ord,
V: std::cmp::Ord,
{
fn contains(&self, other: &BTreeMap<K, V>) -> bool;
fn contains_entry(&self, key: &K, value: &V) -> bool;
}
impl<K, V> TreeMapExtension<K, V> for BTreeMap<K, V>
where
K: std::cmp::Ord,
V: std::cmp::Ord,
{
fn contains(&self, other: &BTreeMap<K, V>) -> bool {
other.iter().all(|(k, v)| self.contains_entry(k, v))
}
fn contains_entry(&self, key: &K, value: &V) -> bool {
self.iter()
.any(|(k, v)| k.cmp(key) == Ordering::Equal && v.cmp(value) == Ordering::Equal)
}
}
pub(crate) trait StringTreeMapExtension {
fn contains_with_case_insensitive_key(&self, other: &BTreeMap<String, String>) -> bool;
fn contains_entry_with_case_insensitive_key(&self, key: &str, value: &str) -> bool;
fn contains_case_insensitive_key(&self, key: &str) -> bool;
fn get_case_insensitive(&self, key: &str) -> Option<&'_ String>;
}
impl StringTreeMapExtension for BTreeMap<String, String> {
fn contains_with_case_insensitive_key(&self, other: &BTreeMap<String, String>) -> bool {
other
.iter()
.all(|(k, v)| self.contains_entry_with_case_insensitive_key(k, v))
}
fn contains_entry_with_case_insensitive_key(&self, key: &str, value: &str) -> bool {
self.iter().any(|(k, v)| {
k.to_lowercase().cmp(&key.to_lowercase()) == Ordering::Equal
&& v.as_str().cmp(value) == Ordering::Equal
})
}
fn contains_case_insensitive_key(&self, key: &str) -> bool {
let key_lc = key.to_lowercase();
self.keys().any(|k| k.to_lowercase().eq(&key_lc))
}
fn get_case_insensitive(&self, key: &str) -> Option<&'_ String> {
let key_lc = key.to_lowercase();
self.keys()
.find(|k| k.to_lowercase().eq(&key_lc))
.map(|key| self.get(key).unwrap())
}
}
#[cfg(test)]
mod test {
use std::collections::BTreeMap;
use crate::server::util::{StringTreeMapExtension, TreeMapExtension};
#[test]
fn tree_map_fully_contains_other() {
let mut m1 = BTreeMap::new();
m1.insert("h1", "v1");
m1.insert("h2", "v2");
let mut m2 = BTreeMap::new();
m2.insert("h1", "v1");
m2.insert("h2", "v2");
let result = m1.contains(&m2);
assert_eq!(true, result);
}
#[test]
fn tree_map_contains_subset() {
let mut m1 = BTreeMap::new();
m1.insert("h1", "v1");
m1.insert("h2", "v2");
let mut m2 = BTreeMap::new();
m2.insert("h1", "v1");
let result = m1.contains(&m2);
assert_eq!(true, result);
}
#[test]
fn tree_map_does_not_contain_other() {
let mut m1 = BTreeMap::new();
m1.insert("h1", "v1");
let mut m2 = BTreeMap::new();
m2.insert("h1", "v1");
m2.insert("h2", "v2");
let result = m1.contains(&m2);
assert_eq!(false, result);
}
#[test]
fn tree_map_contains_all_keys_no_values() {
let mut m1 = BTreeMap::new();
m1.insert("h1", "v1");
m1.insert("h2", "v2");
let mut m2 = BTreeMap::new();
m2.insert("h1", "v3");
m2.insert("h2", "v4");
let result = m1.contains(&m2);
assert_eq!(false, result);
}
#[test]
fn tree_map_contains_all_keys_some_values() {
let mut m1 = BTreeMap::new();
m1.insert("h1", "v1");
m1.insert("h2", "v2");
let mut m2 = BTreeMap::new();
m2.insert("h1", "v1");
m2.insert("h2", "v2");
m2.insert("h3", "v3");
let result = m1.contains(&m2);
assert_eq!(false, result);
}
#[test]
fn tree_map_contains_all_keys_some_values_equal_length() {
let mut m1 = BTreeMap::new();
m1.insert("h1", "v1");
m1.insert("h2", "v2");
let mut m2 = BTreeMap::new();
m2.insert("h1", "v1");
m2.insert("h2", "v3");
let result = m1.contains(&m2);
assert_eq!(false, result);
}
#[test]
fn string_tree_map_contains_all_keys_some_values_equal_length() {
let mut m1 = BTreeMap::new();
m1.insert("h1".to_string(), "v1".to_string());
m1.insert("h2".to_string(), "v2".to_string());
let mut m2 = BTreeMap::new();
m2.insert("H1".to_string(), "v1".to_string());
m2.insert("H2".to_string(), "v2".to_string());
let result = m1.contains_with_case_insensitive_key(&m2);
assert_eq!(true, result);
}
}