use std::collections::HashMap;
use super::super::Axis;
pub(super) fn generate_combos(axes: &[Axis]) -> Vec<Vec<serde_json::Value>> {
let mut combos: Vec<Vec<serde_json::Value>> = vec![vec![]];
for axis in axes {
let mut next = Vec::new();
for existing in &combos {
for value in &axis.values {
let mut combo = existing.clone();
combo.push(value.clone());
next.push(combo);
}
}
combos = next;
}
combos
}
pub(super) struct AxisGroup {
pub parent_axis_idx: usize,
pub children: Vec<usize>,
}
pub(super) fn classify_axes(axes: &[Axis]) -> (Vec<usize>, Vec<AxisGroup>) {
let mut top_level: Vec<usize> = Vec::new();
let mut group_map: HashMap<String, AxisGroup> = HashMap::new();
for (i, axis) in axes.iter().enumerate() {
if let Some(pos) = axis.path.find(".$.") {
let parent_path = axis.path[..pos].to_string();
if let Some(parent_idx) = axes.iter().position(|a| a.path == parent_path) {
let group = group_map
.entry(parent_path)
.or_insert_with(|| AxisGroup { parent_axis_idx: parent_idx, children: Vec::new() });
group.children.push(i);
} else {
top_level.push(i);
}
} else {
top_level.push(i);
}
}
(top_level, group_map.into_values().collect())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn make_axis(path: &str, kind: &str, values: Vec<serde_json::Value>) -> Axis {
Axis { path: path.to_string(), kind: kind.to_string(), values }
}
#[test]
fn combos_single_axis() {
let axes = vec![make_axis("x", "boolean", vec![json!(true), json!(false)])];
let combos = generate_combos(&axes);
assert_eq!(combos.len(), 2);
assert_eq!(combos[0], vec![json!(true)]);
assert_eq!(combos[1], vec![json!(false)]);
}
#[test]
fn combos_cartesian_product() {
let axes = vec![
make_axis("a", "boolean", vec![json!(true), json!(false)]),
make_axis("b", "enum", vec![json!("x"), json!("y"), json!("z")]),
];
let combos = generate_combos(&axes);
assert_eq!(combos.len(), 6);
assert_eq!(combos[0], vec![json!(true), json!("x")]);
assert_eq!(combos[5], vec![json!(false), json!("z")]);
}
#[test]
fn combos_empty_axes() {
let combos = generate_combos(&[]);
assert_eq!(combos.len(), 1);
assert!(combos[0].is_empty());
}
#[test]
fn classify_all_top_level() {
let axes = vec![
make_axis("a", "boolean", vec![json!(true), json!(false)]),
make_axis("b", "enum", vec![json!("x"), json!("y")]),
];
let (top, groups) = classify_axes(&axes);
assert_eq!(top, vec![0, 1]);
assert!(groups.is_empty());
}
#[test]
fn classify_mixed() {
let axes = vec![
make_axis("posts", "array", vec![json!("populated"), json!("empty")]),
make_axis("posts.$.visible", "boolean", vec![json!(true), json!(false)]),
make_axis("title", "nullable", vec![json!("present"), json!(null)]),
];
let (top, groups) = classify_axes(&axes);
assert!(top.contains(&0));
assert!(top.contains(&2));
assert!(!top.contains(&1));
assert_eq!(groups.len(), 1);
assert_eq!(groups[0].parent_axis_idx, 0);
assert_eq!(groups[0].children, vec![1]);
}
}