Skip to content

Commit 2498b97

Browse files
merge develop: integrate NotSummed wrapper + aggregate_count exposure
Two PRs landed on develop while this PR was open: - #659: Element::NotSummed wrapper variant - #658: aggregate_count proof verification under verify feature Conflicts resolved in 8 files. The substantive resolution work: DISCRIMINANT COLLISION. The shipped cidx PR used ElementType discriminant 16 for CountIndexedTree and 17 for ProvableCountIndexedTree. Develop's NotSummed PR allocated byte 16 as NOT_SUMMED_WRAPPER_DISCRIMINANT. Both are still pre-shipping, so renumbering is safe — and since the NotSummed wrapper byte semantics need to round-trip across the wire, the wrapper byte stays at 16 and the cidx discriminants shift: CountIndexedTree: 16 → 17 ProvableCountIndexedTree: 17 → 18 NonCountedCountIndexedTree: 144 → 145 (= 0x80 | 17) NonCountedProvableCountIndexedTree: 145 → 146 (= 0x80 | 18) ENUM VARIANT ORDER. Bincode encodes Element variants by ENUM ORDER (not by ElementType discriminant). The Element enum has been reordered so NotSummed appears at variant index 16 (matching NOT_SUMMED_WRAPPER_DISCRIMINANT), and CountIndexedTree / ProvableCountIndexedTree appear AFTER NotSummed at indices 17/18 — matching their new ElementType discriminants. Without this reordering, bincode would still write 16 for CountIndexedTree but from_serialized_value would interpret 16 as the NotSummed wrapper. WRAPPER NESTING CHECK in `from_serialized_value`: now explicitly rejects both nested NonCounted and cross-nesting with NotSummed (inner_byte == 15 || inner_byte == 16), in addition to the existing reject-high-bit-twin guard. Other resolutions are straightforward additions: both branches added arms to match expressions across helpers.rs, tree_type.rs, visualize.rs; merged additively. The two `mod tests` blocks in grovedb-element/src/element/mod.rs (one from each PR) renamed the cidx-side block to `cidx_tests` to avoid duplicate name. All 1944+ workspace tests pass post-merge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 parents 21cf382 + dbd83dc commit 2498b97

33 files changed

Lines changed: 2108 additions & 205 deletions

grovedb-element/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ verify = []
2727
constructor = []
2828
serde = ["dep:serde"]
2929
visualize = ["dep:grovedb-visualize"]
30+
31+
[dev-dependencies]
32+
serde_json = "1"

grovedb-element/src/element/constructor.rs

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -429,24 +429,71 @@ impl Element {
429429
/// Wrap an element in `NonCounted` so it contributes 0 to its parent count
430430
/// tree's aggregate count when inserted. Sums (if any) still propagate.
431431
///
432-
/// Returns `InvalidInput` if `inner` is itself a `NonCounted`. Wrapping
433-
/// is not idempotent at the type level — use `into_non_counted` to wrap
434-
/// without that risk.
432+
/// Returns `InvalidInput` if `inner` is already wrapped in any wrapper
433+
/// variant (`NonCounted` or `NotSummed`) — the wrappers are mutually
434+
/// exclusive and may not nest in either direction. Use
435+
/// `into_non_counted` to wrap idempotently when `inner` may already be
436+
/// `NonCounted`; use that helper's `Result` return for the
437+
/// cross-wrapper case.
435438
pub fn new_non_counted(inner: Element) -> Result<Self, ElementError> {
436-
if matches!(inner, Element::NonCounted(_)) {
439+
if matches!(inner, Element::NonCounted(_) | Element::NotSummed(_)) {
437440
return Err(ElementError::InvalidInput(
438-
"NonCounted cannot wrap another NonCounted",
441+
"NonCounted cannot wrap another wrapper",
439442
));
440443
}
441444
Ok(Element::NonCounted(Box::new(inner)))
442445
}
443446

444-
/// Wrap `self` in `NonCounted`. If `self` is already `NonCounted`, returns
445-
/// `self` unchanged (idempotent).
446-
pub fn into_non_counted(self) -> Self {
447+
/// Wrap `self` in `NonCounted`. If `self` is already `NonCounted`,
448+
/// returns it unchanged (idempotent on `NonCounted`).
449+
///
450+
/// Returns `InvalidInput` if `self` is `NotSummed` — the two wrappers
451+
/// are mutually exclusive. Callers that need the unconditional wrapping
452+
/// path should ensure the input is a non-wrapper variant before calling.
453+
pub fn into_non_counted(self) -> Result<Self, ElementError> {
454+
match self {
455+
Element::NonCounted(_) => Ok(self),
456+
Element::NotSummed(_) => Err(ElementError::InvalidInput(
457+
"cannot wrap NotSummed in NonCounted; wrappers are mutually exclusive",
458+
)),
459+
other => Ok(Element::NonCounted(Box::new(other))),
460+
}
461+
}
462+
463+
/// Wrap a sum-tree variant in `NotSummed` so it contributes 0 to its
464+
/// parent sum tree's running sum when inserted. Counts (if any) still
465+
/// propagate.
466+
///
467+
/// Only the four sum-tree variants are accepted: `SumTree`, `BigSumTree`,
468+
/// `CountSumTree`, `ProvableCountSumTree`. Any other element — including
469+
/// items, sum items, references, non-sum trees, and any wrapper
470+
/// (`NonCounted`, `NotSummed`) — is rejected with `InvalidInput`.
471+
pub fn new_not_summed(inner: Element) -> Result<Self, ElementError> {
472+
match inner {
473+
Element::SumTree(..)
474+
| Element::BigSumTree(..)
475+
| Element::CountSumTree(..)
476+
| Element::ProvableCountSumTree(..) => Ok(Element::NotSummed(Box::new(inner))),
477+
_ => Err(ElementError::InvalidInput(
478+
"NotSummed inner element must be a sum-tree variant (SumTree, BigSumTree, \
479+
CountSumTree, or ProvableCountSumTree)",
480+
)),
481+
}
482+
}
483+
484+
/// Wrap `self` in `NotSummed`. If `self` is already `NotSummed`, returns
485+
/// it unchanged (idempotent on `NotSummed`).
486+
///
487+
/// Returns `InvalidInput` if `self` is `NonCounted` (the two wrappers
488+
/// are mutually exclusive) or any non-sum-tree variant. Mirrors
489+
/// [`Element::into_non_counted`].
490+
pub fn into_not_summed(self) -> Result<Self, ElementError> {
447491
match self {
448-
Element::NonCounted(_) => self,
449-
other => Element::NonCounted(Box::new(other)),
492+
Element::NotSummed(_) => Ok(self),
493+
Element::NonCounted(_) => Err(ElementError::InvalidInput(
494+
"cannot wrap NonCounted in NotSummed; wrappers are mutually exclusive",
495+
)),
496+
other => Self::new_not_summed(other),
450497
}
451498
}
452499
}

0 commit comments

Comments
 (0)