use std::collections::BTreeMap;
use serde_json::Value;
use sha2::{Digest, Sha256};
use super::blocking::BlockingKey;
use super::component::Stored;
pub(super) fn object_entries(value: &Value) -> impl Iterator<Item = (&String, &Value)> {
value
.as_object()
.into_iter()
.flat_map(serde_json::Map::iter)
}
pub(super) fn canonical_scalar(value: &Value) -> Option<String> {
match value {
Value::String(s) => Some(s.clone()),
Value::Bool(b) => Some(b.to_string()),
Value::Number(n) => Some(n.to_string()),
Value::Null | Value::Array(_) | Value::Object(_) => None,
}
}
#[allow(clippy::cast_possible_truncation)] pub(super) fn value_to_i64(value: &Value) -> Option<i64> {
value
.as_i64()
.or_else(|| value.as_f64().map(|f| f.round() as i64))
}
pub(super) fn group_key(
stored: &BTreeMap<String, Stored>,
namespace: &[u8],
members: &[&str],
) -> Option<BlockingKey> {
let mut hasher = Sha256::new();
hasher.update(namespace);
for member in members {
let bytes = scalar_bytes(stored.get(*member)?)?;
hasher.update((bytes.len() as u64).to_le_bytes());
hasher.update(&bytes);
}
Some(hasher.finalize().into())
}
pub(super) fn all_scalar_key(stored: &BTreeMap<String, Stored>) -> Option<BlockingKey> {
let mut hasher = Sha256::new();
hasher.update(b"K0");
let mut any = false;
for (name, value) in stored {
let Some(bytes) = scalar_bytes(value) else {
continue;
};
any = true;
hasher.update((name.len() as u64).to_le_bytes());
hasher.update(name.as_bytes());
hasher.update((bytes.len() as u64).to_le_bytes());
hasher.update(&bytes);
}
any.then(|| hasher.finalize().into())
}
fn scalar_bytes(stored: &Stored) -> Option<Vec<u8>> {
match stored {
Stored::Category(hash) => Some(hash.to_vec()),
Stored::Numeric(n) => Some(n.to_le_bytes().to_vec()),
Stored::Set(_) => None,
}
}