use indexmap::IndexMap;
use crate::ast::Value;
#[derive(Clone, Debug, Default)]
pub struct ScopeCoord {
pub vars: IndexMap<String, Value>,
}
impl ScopeCoord {
pub fn new() -> Self {
Self {
vars: IndexMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.vars.is_empty()
}
pub fn len(&self) -> usize {
self.vars.len()
}
}
impl<I> From<I> for ScopeCoord
where
I: IntoIterator<Item = (String, Value)>,
{
fn from(it: I) -> Self {
Self {
vars: it.into_iter().collect(),
}
}
}
pub fn format_scope_coordinate_path(path: &[ScopeCoord]) -> String {
let strata: Vec<String> = path
.iter()
.filter(|c| !c.is_empty())
.map(|coord| {
let inner = coord
.vars
.iter()
.map(|(k, v)| format!("{k}={}", v.to_display_string()))
.collect::<Vec<_>>()
.join(", ");
format!("({inner})")
})
.collect();
strata.join(", ")
}
#[cfg(test)]
mod tests {
use super::*;
fn coord(pairs: &[(&str, Value)]) -> ScopeCoord {
ScopeCoord::from(
pairs
.iter()
.map(|(k, v)| ((*k).to_string(), v.clone()))
.collect::<Vec<_>>(),
)
}
#[test]
fn a_path_renders_leaf_first_in_declaration_order() {
let path = [
coord(&[
("k", Value::U64(10)),
("limit", Value::U64(20)),
("label", Value::Str("hot".into())),
]),
coord(&[("table", Value::Str("users".into()))]),
];
assert_eq!(
format_scope_coordinate_path(&path),
"(k=10, limit=20, label=hot), (table=users)"
);
}
#[test]
fn an_empty_stratum_is_omitted_anywhere_in_the_path() {
let outer = coord(&[("phase", Value::U64(1))]);
let inner = coord(&[("k", Value::U64(3))]);
let empty = ScopeCoord::new();
assert_eq!(
format_scope_coordinate_path(&[inner.clone(), empty.clone(), outer.clone()]),
"(k=3), (phase=1)"
);
assert_eq!(
format_scope_coordinate_path(&[empty.clone(), inner]),
"(k=3)"
);
assert_eq!(format_scope_coordinate_path(&[empty]), "");
}
#[test]
fn an_empty_path_is_the_empty_string() {
assert_eq!(format_scope_coordinate_path(&[]), "");
}
#[test]
fn the_same_coordinates_render_to_the_same_string() {
let a = [coord(&[("k", Value::U64(7)), ("m", Value::F64(1.5))])];
let b = [coord(&[("k", Value::U64(7)), ("m", Value::F64(1.5))])];
assert_eq!(
format_scope_coordinate_path(&a),
format_scope_coordinate_path(&b)
);
let swapped = [coord(&[("m", Value::F64(1.5)), ("k", Value::U64(7))])];
assert_ne!(
format_scope_coordinate_path(&a),
format_scope_coordinate_path(&swapped)
);
}
}