use serde_json::{Map, Value};
use std::collections::HashMap;
pub const MAX_MATRIX_PRODUCT: usize = 10_000;
#[derive(Debug, Clone, PartialEq)]
pub struct Dim {
pub id: String,
pub alias: String,
pub values: Vec<Value>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CollectedDim {
pub id: String,
pub alias: String,
pub dims: Vec<String>,
pub by_tuple: HashMap<String, Vec<Value>>,
}
pub fn collected_tuple_key(dims: &[String], ctx: &HashMap<String, Value>) -> String {
dims.iter()
.map(|d| {
let v = ctx.get(d).cloned().unwrap_or(Value::Null);
format!("{d}={}", serde_json::to_string(&v).unwrap_or_default())
})
.collect::<Vec<_>>()
.join("&")
}
pub fn inject_collected(ctx: &mut HashMap<String, Value>, collected: &[CollectedDim]) {
for cd in collected {
let key = collected_tuple_key(&cd.dims, ctx);
let list = cd.by_tuple.get(&key).cloned().unwrap_or_default();
let mut obj = Map::new();
obj.insert(cd.alias.clone(), Value::Array(list));
ctx.insert(cd.id.clone(), Value::Object(obj));
}
}
pub fn project_dedup(records: &[Value], select: &str) -> Vec<Value> {
let mut seen: Vec<Value> = Vec::new();
for rec in records {
let Some(v) = project_value(rec, select) else {
continue;
};
if v.is_null() {
continue;
}
if !seen.contains(&v) {
seen.push(v);
}
}
seen
}
fn project_value(record: &Value, select: &str) -> Option<Value> {
let path = select
.strip_prefix("$.")
.or_else(|| select.strip_prefix('$'))
.unwrap_or(select);
if path.is_empty() {
return Some(record.clone());
}
let mut cur = record;
for seg in path.split('.') {
cur = cur.get(seg)?;
}
Some(cur.clone())
}
pub fn product_size(dims: &[Dim]) -> usize {
if dims.is_empty() {
return 0;
}
dims.iter()
.map(|d| d.values.len())
.try_fold(1usize, |acc, n| acc.checked_mul(n))
.unwrap_or(usize::MAX)
}
pub fn cartesian(dims: &[Dim]) -> Vec<HashMap<String, Value>> {
if dims.is_empty() || dims.iter().any(|d| d.values.is_empty()) {
return Vec::new();
}
let mut out: Vec<HashMap<String, Value>> = vec![HashMap::new()];
for dim in dims {
let mut next = Vec::with_capacity(out.len() * dim.values.len());
for base in &out {
for v in &dim.values {
let mut ctx = base.clone();
let mut obj = Map::new();
obj.insert(dim.alias.clone(), v.clone());
ctx.insert(dim.id.clone(), Value::Object(obj));
next.push(ctx);
}
}
out = next;
}
out
}
pub fn tuple_state_key_suffix(dims: &[Dim], ctx: &HashMap<String, Value>) -> String {
dims.iter()
.map(|d| {
let v = ctx
.get(&d.id)
.and_then(|o| o.get(&d.alias))
.map(value_brief)
.unwrap_or_else(|| "(missing)".to_string());
format!("{}={}", d.alias, v)
})
.collect::<Vec<_>>()
.join("&")
}
fn value_brief(v: &Value) -> String {
match v {
Value::String(s) => s.clone(),
Value::Null => "null".to_string(),
other => other.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn project_dedup_dotpath_and_dollar() {
let recs = vec![
json!({"id": 1, "name": "a"}),
json!({"id": 2, "name": "b"}),
json!({"id": 1, "name": "c"}), ];
assert_eq!(project_dedup(&recs, "$.id"), vec![json!(1), json!(2)]);
assert_eq!(project_dedup(&recs, "id"), vec![json!(1), json!(2)]);
assert_eq!(project_dedup(&recs, "$").len(), 3);
}
#[test]
fn project_dedup_skips_null_and_missing() {
let recs = vec![
json!({"id": 1}),
json!({"id": null}),
json!({"other": 9}), json!({"id": 2}),
];
assert_eq!(project_dedup(&recs, "$.id"), vec![json!(1), json!(2)]);
}
#[test]
fn project_nested_path() {
let recs = vec![
json!({"meta": {"code": "X"}}),
json!({"meta": {"code": "Y"}}),
];
assert_eq!(
project_dedup(&recs, "$.meta.code"),
vec![json!("X"), json!("Y")]
);
}
fn dim(id: &str, alias: &str, vals: Vec<Value>) -> Dim {
Dim {
id: id.into(),
alias: alias.into(),
values: vals,
}
}
#[test]
fn cartesian_two_dims_is_product() {
let dims = vec![
dim("subs", "subsidiary_id", vec![json!(1), json!(2)]),
dim(
"fields",
"field_id",
vec![json!("a"), json!("b"), json!("c")],
),
];
let ctxs = cartesian(&dims);
assert_eq!(ctxs.len(), 6);
assert_eq!(ctxs[0]["subs"]["subsidiary_id"], json!(1));
assert_eq!(ctxs[0]["fields"]["field_id"], json!("a"));
let pairs: Vec<(Value, Value)> = ctxs
.iter()
.map(|c| {
(
c["subs"]["subsidiary_id"].clone(),
c["fields"]["field_id"].clone(),
)
})
.collect();
assert!(pairs.contains(&(json!(2), json!("c"))));
}
#[test]
fn cartesian_single_dim() {
let dims = vec![dim("d", "v", vec![json!(1), json!(2)])];
let ctxs = cartesian(&dims);
assert_eq!(ctxs.len(), 2);
assert_eq!(ctxs[1]["d"]["v"], json!(2));
}
#[test]
fn cartesian_empty_dim_yields_nothing() {
let dims = vec![dim("a", "x", vec![json!(1)]), dim("b", "y", vec![])];
assert!(cartesian(&dims).is_empty());
assert!(cartesian(&[]).is_empty());
}
#[test]
fn product_size_math() {
assert_eq!(product_size(&[]), 0);
assert_eq!(
product_size(&[
dim("a", "x", vec![json!(1), json!(2)]),
dim("b", "y", vec![json!(1)])
]),
2
);
assert_eq!(
product_size(&[dim("a", "x", vec![]), dim("b", "y", vec![json!(1)])]),
0
);
}
#[test]
fn collected_tuple_key_symmetric_between_store_and_read() {
let dims = vec!["types".to_string()];
let store_ctx: HashMap<String, Value> =
[("types".to_string(), json!({"name": "deal"}))].into();
let read_ctx: HashMap<String, Value> = [
("types".to_string(), json!({"name": "deal"})),
("other".to_string(), json!({"x": 1})),
]
.into();
assert_eq!(
collected_tuple_key(&dims, &store_ctx),
collected_tuple_key(&dims, &read_ctx)
);
}
#[test]
fn inject_collected_puts_the_list_into_ctx() {
let cd = CollectedDim {
id: "props".into(),
alias: "name".into(),
dims: vec!["types".into()],
by_tuple: [(
collected_tuple_key(
&["types".into()],
&[("types".to_string(), json!({"name": "deal"}))].into(),
),
vec![json!("amount"), json!("stage")],
)]
.into(),
};
let mut ctx: HashMap<String, Value> =
[("types".to_string(), json!({"name": "deal"}))].into();
inject_collected(&mut ctx, std::slice::from_ref(&cd));
assert_eq!(ctx["props"]["name"], json!(["amount", "stage"]));
let mut miss: HashMap<String, Value> =
[("types".to_string(), json!({"name": "ticket"}))].into();
inject_collected(&mut miss, &[cd]);
assert_eq!(miss["props"]["name"], json!([]));
}
#[test]
fn tuple_state_key_is_stable_and_ordered() {
let dims = vec![
dim("subs", "subsidiary_id", vec![json!(1)]),
dim("fields", "field_id", vec![json!("a")]),
];
let ctxs = cartesian(&dims);
assert_eq!(
tuple_state_key_suffix(&dims, &ctxs[0]),
"subsidiary_id=1&field_id=a"
);
}
}