From 4897457074182d6ac96a205f34f69eb3bd5f29a0 Mon Sep 17 00:00:00 2001 From: Libby Date: Wed, 11 Nov 2026 07:42:54 +0000 Subject: [PATCH] cultcache-rs: pin the global flag surviving a refused re-registration; correct the changelog's decode claim Co-Authored-By: Claude Sonnet 5.5 --- packages/cultcache-rs/CHANGELOG.md | 2 +- packages/cultcache-rs/tests/lookups.rs | 35 ++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/cultcache-rs/CHANGELOG.md b/packages/cultcache-rs/CHANGELOG.md index d1f56aa6..f136bccf 100644 --- a/packages/cultcache-rs/CHANGELOG.md +++ b/packages/cultcache-rs/CHANGELOG.md @@ -13,7 +13,7 @@ only in git history. - `#[cultcache(index)]` means unique, as in C# `RefuseIndexSharing`: within one type and one alias no two records hold one non-blank value. A put, batch, push or load that would make two records hold a value is refused, naming both keys and the alias, and a store that already holds a duplicate does not open. A grouping index is no longer expressible; a consumer that relied on shared values must drop the `index` and look up by another way. - `bool` index and lookup text renders `True` / `False`, as C# does, where it rendered `true` / `false`. - `DatabaseEntry::UNIQUE_INDEXES` replaces `cult_indexes` as the one index declaration and changes type (its members are the unique aliases); a hand implementation overriding `cult_indexes` no longer compiles. -- `register_entry_type` over a type that already holds records re-judges them under the new declaration (unique indexes and the global rule) and refuses, changing nothing, when they violate it or cannot be decoded. +- `register_entry_type` over a type that already holds records re-judges them under the new declaration (unique indexes and the global rule) and refuses, changing nothing, when they violate it. A declaration with an index also refuses when a held record cannot be decoded as the new type; a declaration without one does not read the records, so it does not. ### Added diff --git a/packages/cultcache-rs/tests/lookups.rs b/packages/cultcache-rs/tests/lookups.rs index a0ac546d..29aa3489 100644 --- a/packages/cultcache-rs/tests/lookups.rs +++ b/packages/cultcache-rs/tests/lookups.rs @@ -241,14 +241,45 @@ fn registering_a_type_global_over_two_held_records_is_refused_and_changes_nothin assert_eq!(cache.get_all_with_keys::().unwrap().len(), 3, "still ordinary"); } -// One held record may become the global; dropping the rule again restores the ordinary type. +// One held record may become the global; a second is then refused as a second global. #[test] fn registering_a_type_global_over_one_held_record_is_admitted() { let mut cache = CultCache::new(); cache.register_entry_type::().unwrap(); cache.put("a", &LooseSettings { value: "one".into() }).unwrap(); cache.register_entry_type::().unwrap(); - assert!(cache.put("b", &Settings { value: "two".into() }).is_err(), "a second global is refused"); + let refusal = cache.put("b", &Settings { value: "two".into() }).unwrap_err(); + assert!(refusal.downcast_ref::().is_some(), "{refusal:#}"); +} + +/// A global with an indexed member, and the same type and schema declaring that member as an integer. +#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] +#[cultcache(type = "indexed_global", schema = "tests.indexed_global", global)] +struct IndexedGlobal { + #[cultcache(key = 0, index = "V")] + v: String, +} + +#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] +#[cultcache(type = "indexed_global", schema = "tests.indexed_global", global)] +struct RetypedGlobal { + #[cultcache(key = 0, index = "V")] + v: i64, +} + +// A refused re-registration over an already-global type leaves it global: the rule keeps refusing a second record. +#[test] +fn a_refused_reregistration_leaves_a_global_type_global() { + let mut cache = CultCache::new(); + cache.register_entry_type::().unwrap(); + cache.put("a", &IndexedGlobal { v: "x".into() }).unwrap(); + + assert!(cache.register_entry_type::().is_err(), "the held text cannot be decoded as an integer"); + + let refusal = cache.put("b", &IndexedGlobal { v: "y".into() }).unwrap_err(); + assert!(refusal.downcast_ref::().is_some(), "{refusal:#}"); + assert_eq!(cache.get_global::().unwrap(), Some(IndexedGlobal { v: "x".into() })); + assert_eq!(cache.get_by_index::("V", "x").unwrap(), cache.get::("a").unwrap()); } #[test]