use crate::{path, Path, Reader, Record, Store, Value};
pub fn check_conventions<S: Store>(store: &mut S) {
check_leaf_roundtrip(store);
check_missing_reads_none(store);
check_deep_write_creates_intermediates(store);
check_prefix_read_returns_children(store);
check_read_children(store);
check_null_write_deletes_subtree(store);
check_map_write_replaces_subtree(store);
}
fn read_value<S: Reader>(store: &mut S, path: &Path) -> Option<Value> {
store
.read(path)
.unwrap_or_else(|e| panic!("read {} failed: {}", path, e))
.map(|record| {
record
.as_value()
.unwrap_or_else(|| panic!("read {} returned a raw record", path))
.clone()
})
}
pub fn check_leaf_roundtrip<S: Store>(store: &mut S) {
let p = path!("conformance_leaf/value");
store
.write(&p, Record::parsed(Value::from("roundtrip")))
.expect("leaf write failed");
assert_eq!(
read_value(store, &p),
Some(Value::from("roundtrip")),
"leaf write/read did not roundtrip"
);
}
pub fn check_missing_reads_none<S: Store>(store: &mut S) {
assert_eq!(
read_value(store, &path!("conformance_missing/never_written")),
None,
"missing path must read as None, not an error or a value"
);
}
pub fn check_deep_write_creates_intermediates<S: Store>(store: &mut S) {
store
.write(
&path!("conformance_deep/a/b/c"),
Record::parsed(Value::from(1i64)),
)
.expect("deep write must create intermediate maps");
for prefix in [
"conformance_deep",
"conformance_deep/a",
"conformance_deep/a/b",
] {
let p = Path::parse(prefix).unwrap();
assert!(
matches!(read_value(store, &p), Some(Value::Map(_))),
"intermediate {} must exist as a map after a deep write",
prefix
);
}
}
pub fn check_prefix_read_returns_children<S: Store>(store: &mut S) {
store
.write(
&path!("conformance_tree/users/alice"),
Record::parsed(Value::from(1i64)),
)
.expect("write failed");
store
.write(
&path!("conformance_tree/users/bob"),
Record::parsed(Value::from(2i64)),
)
.expect("write failed");
match read_value(store, &path!("conformance_tree/users")) {
Some(Value::Map(map)) => {
assert!(
map.contains_key("alice") && map.contains_key("bob"),
"prefix read must include children as map keys, got: {:?}",
map.keys().collect::<Vec<_>>()
);
}
other => panic!(
"prefix read must return a map of children, got: {:?}",
other
),
}
}
pub fn check_read_children<S: Store>(store: &mut S) {
store
.write(
&path!("conformance_children/x"),
Record::parsed(Value::from(1i64)),
)
.expect("write failed");
store
.write(
&path!("conformance_children/y"),
Record::parsed(Value::from(2i64)),
)
.expect("write failed");
let mut children = store
.read_children(&path!("conformance_children"))
.expect("read_children failed")
.expect("read_children must return Some at an existing prefix");
children.sort();
assert_eq!(
children,
vec!["x".to_string(), "y".to_string()],
"read_children must enumerate direct children"
);
assert_eq!(
store
.read_children(&path!("conformance_children_missing"))
.expect("read_children failed"),
None,
"read_children at a missing path must return None"
);
}
pub fn check_null_write_deletes_subtree<S: Store>(store: &mut S) {
store
.write(
&path!("conformance_del/accounts/personal/key"),
Record::parsed(Value::from("secret")),
)
.expect("write failed");
store
.write(
&path!("conformance_del/accounts_other"),
Record::parsed(Value::from("survivor")),
)
.expect("write failed");
store
.write(
&path!("conformance_del/accounts"),
Record::parsed(Value::Null),
)
.expect("null write failed");
assert_eq!(
read_value(store, &path!("conformance_del/accounts")),
None,
"null write must delete the node"
);
assert_eq!(
read_value(store, &path!("conformance_del/accounts/personal/key")),
None,
"null write must delete the entire subtree"
);
assert_eq!(
read_value(store, &path!("conformance_del/accounts_other")),
Some(Value::from("survivor")),
"null write must be component-wise: string-prefix siblings survive"
);
}
pub fn check_map_write_replaces_subtree<S: Store>(store: &mut S) {
store
.write(
&path!("conformance_replace/cfg/stale"),
Record::parsed(Value::from("old")),
)
.expect("write failed");
let mut fresh = std::collections::BTreeMap::new();
fresh.insert("fresh".to_string(), Value::from("new"));
store
.write(
&path!("conformance_replace/cfg"),
Record::parsed(Value::Map(fresh)),
)
.expect("map write failed");
assert_eq!(
read_value(store, &path!("conformance_replace/cfg/stale")),
None,
"map write at a parent must sweep stale descendants"
);
assert_eq!(
read_value(store, &path!("conformance_replace/cfg/fresh")),
Some(Value::from("new")),
"map write must install the new state"
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::MemoryStore;
#[test]
fn memory_store_is_conformant() {
check_conventions(&mut MemoryStore::new());
}
}