use crate::{DslEntry, DslMap};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum RegionCategory {
Global,
Wildcard(String),
Region(String),
Anonymous(String),
}
impl RegionCategory {
fn from_region_name(name: &str) -> Self {
if name == "$global" {
RegionCategory::Global
} else if name.contains('*') {
RegionCategory::Wildcard(name.to_string())
} else if name.starts_with("__anon_") {
RegionCategory::Anonymous(name.to_string())
} else {
RegionCategory::Region(name.to_string())
}
}
}
fn compare_region_keys(a: &str, b: &str) -> std::cmp::Ordering {
use std::cmp::Ordering;
let cat_a = RegionCategory::from_region_name(a);
let cat_b = RegionCategory::from_region_name(b);
match cat_a.cmp(&cat_b) {
Ordering::Equal => {
a.cmp(b)
}
other => other,
}
}
pub fn apply_deterministic_ordering(dsl_map: BTreeMap<String, DslEntry>) -> DslMap {
let mut entries: Vec<(String, DslEntry)> = dsl_map.into_iter().collect();
entries.sort_by(|a, b| compare_region_keys(&a.0, &b.0));
let mut ordered_map = BTreeMap::new();
for (key, entry) in entries {
ordered_map.insert(key, entry);
}
ordered_map
}
pub fn shape_final_output(dsl_map: BTreeMap<String, DslEntry>) -> DslMap {
let filtered_map: BTreeMap<String, DslEntry> = dsl_map
.into_iter()
.filter(|(key, entry)| {
if !key.starts_with("__anon_") {
return true;
}
!entry.metadata.is_empty()
})
.collect();
apply_deterministic_ordering(filtered_map)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::collections::BTreeMap;
fn make_entry(
boxes: Option<Vec<([i32; 3], [i32; 3])>>,
metadata: &[(&str, serde_json::Value)],
) -> DslEntry {
let mut meta_map = BTreeMap::new();
for (key, value) in metadata {
meta_map.insert(key.to_string(), value.clone());
}
DslEntry {
bounding_boxes: boxes,
metadata: meta_map,
}
}
#[test]
fn test_region_category_ordering() {
let mut categories = [
RegionCategory::Region("zebra".to_string()),
RegionCategory::Anonymous("__anon_0_0".to_string()),
RegionCategory::Wildcard("cpu.*".to_string()),
RegionCategory::Global,
RegionCategory::Region("apple".to_string()),
RegionCategory::Wildcard("*.cache".to_string()),
];
categories.sort();
assert!(matches!(categories[0], RegionCategory::Global));
assert!(matches!(categories[1], RegionCategory::Wildcard(_)));
assert!(matches!(categories[2], RegionCategory::Wildcard(_)));
assert!(matches!(categories[3], RegionCategory::Region(_)));
assert!(matches!(categories[4], RegionCategory::Region(_)));
assert!(matches!(categories[5], RegionCategory::Anonymous(_)));
if let RegionCategory::Wildcard(ref name) = categories[1] {
assert!(name.starts_with("*.cache") || name.starts_with("cpu.*"));
}
if let RegionCategory::Region(ref name) = categories[3] {
assert_eq!(name, "apple");
}
if let RegionCategory::Region(ref name) = categories[4] {
assert_eq!(name, "zebra");
}
}
#[test]
fn test_deterministic_ordering() {
let mut dsl_map = BTreeMap::new();
dsl_map.insert(
"zebra".to_string(),
make_entry(Some(vec![([0, 0, 0], [1, 1, 1])]), &[]),
);
dsl_map.insert(
"$global".to_string(),
make_entry(None, &[("version", json!("1.0"))]),
);
dsl_map.insert(
"cpu.*".to_string(),
make_entry(None, &[("power", json!("low"))]),
);
dsl_map.insert(
"apple".to_string(),
make_entry(Some(vec![([2, 2, 2], [3, 3, 3])]), &[]),
);
dsl_map.insert(
"*.cache".to_string(),
make_entry(None, &[("size", json!(1024))]),
);
dsl_map.insert(
"__anon_0_0".to_string(),
make_entry(
Some(vec![([4, 4, 4], [5, 5, 5])]),
&[("label", json!("anon"))],
),
);
let ordered_map = apply_deterministic_ordering(dsl_map);
let keys: Vec<&String> = ordered_map.keys().collect();
assert_eq!(
keys,
vec![
&"$global".to_string(), &"*.cache".to_string(), &"__anon_0_0".to_string(), &"apple".to_string(), &"cpu.*".to_string(), &"zebra".to_string(), ]
);
}
#[test]
fn test_filter_anonymous_without_metadata() {
let mut dsl_map = BTreeMap::new();
dsl_map.insert(
"__anon_0_0".to_string(),
make_entry(Some(vec![([0, 0, 0], [1, 1, 1])]), &[]),
);
dsl_map.insert(
"__anon_0_1".to_string(),
make_entry(
Some(vec![([2, 2, 2], [3, 3, 3])]),
&[("label", json!("kept"))],
),
);
dsl_map.insert(
"named".to_string(),
make_entry(Some(vec![([4, 4, 4], [5, 5, 5])]), &[]),
);
let shaped_map = shape_final_output(dsl_map);
assert!(!shaped_map.contains_key("__anon_0_0")); assert!(shaped_map.contains_key("__anon_0_1")); assert!(shaped_map.contains_key("named")); }
#[test]
fn test_shape_final_output_complete() {
let mut dsl_map = BTreeMap::new();
dsl_map.insert(
"region_z".to_string(),
make_entry(Some(vec![([0, 0, 0], [1, 1, 1])]), &[]),
);
dsl_map.insert(
"$global".to_string(),
make_entry(None, &[("version", json!("1.0"))]),
);
dsl_map.insert(
"cpu.*".to_string(),
make_entry(None, &[("power", json!("low"))]),
);
dsl_map.insert(
"region_a".to_string(),
make_entry(
Some(vec![([2, 2, 2], [3, 3, 3])]),
&[("type", json!("test"))],
),
);
dsl_map.insert(
"__anon_0_0".to_string(),
make_entry(Some(vec![([4, 4, 4], [5, 5, 5])]), &[]),
); dsl_map.insert(
"__anon_0_1".to_string(),
make_entry(
Some(vec![([6, 6, 6], [7, 7, 7])]),
&[("anon_label", json!("kept"))],
),
); dsl_map.insert(
"*.cache".to_string(),
make_entry(None, &[("size", json!(2048))]),
);
let shaped_map = shape_final_output(dsl_map);
let keys: Vec<&String> = shaped_map.keys().collect();
assert_eq!(
keys,
vec![
&"$global".to_string(), &"*.cache".to_string(), &"__anon_0_1".to_string(), &"cpu.*".to_string(), &"region_a".to_string(), &"region_z".to_string(),
]
);
assert!(!shaped_map.contains_key("__anon_0_0"));
}
}