feat(collections): introduce Object storage abstraction (foundation) - #57
Conversation
|
@copilot review this pr using all the review skills in this repo. Launch each skill in a separate agent and report the findings here. Do not make any commits to the branch. |
Completed review using both review skills in separate agents:
Both reports were generated and saved in-agent:
No commits were made. |
cad5fe8 to
dcaf050
Compare
|
@copilot Review this PR using all the review skills in this repo. Use a separate agent for each skill. |
There was a problem hiding this comment.
Pull request overview
Introduces an opaque Object storage type wrapping BTreeMap<Value, Value> to allow alternative backends (lazy, inline small-map, alias-aware, etc.) to be swapped or coexist without changing call sites. This is the foundation PR; payload swap and call-site migration follow in subsequent PRs.
Changes:
- New
src/value/object.rsmodule withObject,Iter,IterMut,IntoIter, andObjectCursor(resumable iteration), plus serde,Ord/PartialOrd(defined againstiter_sortedfor storage-independent comparison), andIndeximpls. - Re-exports
Objectfromregorusand convertssrc/value.rs→src/value/mod.rsto host the new submodule; tests added insrc/value/tests.rs. - Updates
src/scheduler.rsto qualifyExpr::Array/Expr::Objectpatterns to disambiguate from the new top-levelObjectre-export (whereuse crate::*+use crate::ast::Expr::*were both in scope). - New design doc
docs/value/object.md.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/value/object.rs | New opaque Object wrapper around BTreeMap, with curated API, resumable cursor, hand-written Ord over sorted iter, serde (canonical sorted output), and iterator types. |
| src/value/mod.rs | Wires in new object submodule and tests; re-exports Object/iterator types (and ObjectCursor under rvm). |
| src/value/tests.rs | Unit tests: oracle parity with BTreeMap, dup-key semantics, accessor coverage, serde roundtrip, cursor coverage, Ord invariants. |
| src/lib.rs | Adds Object to public re-exports. |
| src/scheduler.rs | Qualifies Expr::Array/Expr::Object patterns to avoid clash with newly re-exported Object. |
| docs/value/object.md | Design rationale, precedents, and target use cases for the abstraction. |
anakrish
left a comment
There was a problem hiding this comment.
Both lib.rs and scheduler.rs contain unrelated changes and should not be part of this PR.
| #[cfg(test)] | ||
| mod tests; | ||
|
|
||
| #[allow(unused_imports)] // surface for downstream PRs; cursor used internally in PR1b |
There was a problem hiding this comment.
Done in 54d8b66 — comment now reads just // surface for downstream PRs.
Completed. I ran both review skills in separate agents and collected the results:
No commits were made. |
54d8b66 to
1016c83
Compare
553e431 to
98873b8
Compare
Add an opaque Object type for the key→value storage backing Value::Object. It exposes a small set of methods (get, insert, remove, iter, iter_sorted, cursor, serde) and keeps the backing store private, so future representations -- inline small-map, hash-backed, lazy, arena, FFI-callback -- can plug in without touching the call sites that name this type. Nothing in the engine uses Object yet. Value::Object still wraps Rc<BTreeMap<Value, Value>>; the payload swap and call-site migration come in the next PR. Object stands on its own unit tests in the meantime. docs/value/object.md walks through the design, the precedents it follows (serde_json::Map, toml::Table, simdjson DOM), and the concrete workloads the abstraction is meant to unlock. A matching Set abstraction follows in a separate PR. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
98873b8 to
595029d
Compare
|
Re the code-review finding (lossy serde round-trip for non-string keys): this is pre-existing behavior, not a regression in this PR. |
Builds on #57. Swap Value::Object's payload from Rc<BTreeMap<Value, Value>> to Rc<Object> and migrate all call sites to the Object API. as_object / as_object_mut keep their names but return &Object / &mut Object. The mutable accessor handles Rc::make_mut internally, so callers no longer do it themselves. Object grows into_value() and From<Object> for Value. Value's serializer now delegates to Object::serialize, dropping a duplicate non-string-key stringification path. RVM IterationState::Object is rewritten around ObjectCursor: O(log n) steps over a shared Rc<Object>, no eager pair snapshot. Snapshot independence is preserved by Rc copy-on-write; setup_next_iteration advances the cursor inline and advance() becomes a no-op for this variant. A new iteration_state_object_is_snapshot_independent_of_source test covers CoW against a mutated alias. Value::Set still wraps Rc<BTreeSet<Value>>; the matching Set abstraction and its swap ship in follow-up PRs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Builds on #57. Swap Value::Object's payload from Rc<BTreeMap<Value, Value>> to Rc<Object> and migrate all call sites to the Object API. as_object / as_object_mut keep their names but return &Object / &mut Object. The mutable accessor handles Rc::make_mut internally, so callers no longer do it themselves. Object grows into_value() and From<Object> for Value. Value's serializer now delegates to Object::serialize, dropping a duplicate non-string-key stringification path. RVM IterationState::Object is rewritten around ObjectCursor: O(log n) steps over a shared Rc<Object>, no eager pair snapshot. Snapshot independence is preserved by Rc copy-on-write; setup_next_iteration advances the cursor inline and advance() becomes a no-op for this variant. A new iteration_state_object_is_snapshot_independent_of_source test covers CoW against a mutated alias. Value::Set still wraps Rc<BTreeSet<Value>>; the matching Set abstraction and its swap ship in follow-up PRs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Builds on #57. Swap Value::Object's payload from Rc<BTreeMap<Value, Value>> to Rc<Object> and migrate all call sites to the Object API. as_object / as_object_mut keep their names but return &Object / &mut Object. The mutable accessor handles Rc::make_mut internally, so callers no longer do it themselves. Object grows into_value() and From<Object> for Value. Value's serializer now delegates to Object::serialize, dropping a duplicate non-string-key stringification path. RVM IterationState::Object is rewritten around ObjectCursor: O(log n) steps over a shared Rc<Object>, no eager pair snapshot. Snapshot independence is preserved by Rc copy-on-write; setup_next_iteration advances the cursor inline and advance() becomes a no-op for this variant. A new iteration_state_object_is_snapshot_independent_of_source test covers CoW against a mutated alias. Value::Set still wraps Rc<BTreeSet<Value>>; the matching Set abstraction and its swap ship in follow-up PRs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Builds on #57. Swap Value::Object's payload from Rc<BTreeMap<Value, Value>> to Rc<Object> and migrate all call sites to the Object API. as_object / as_object_mut keep their names but return &Object / &mut Object. The mutable accessor handles Rc::make_mut internally, so callers no longer do it themselves. Object grows into_value() and From<Object> for Value. Value's serializer now delegates to Object::serialize, dropping a duplicate non-string-key stringification path. RVM IterationState::Object is rewritten around ObjectCursor: O(log n) steps over a shared Rc<Object>, no eager pair snapshot. Snapshot independence is preserved by Rc copy-on-write; setup_next_iteration advances the cursor inline and advance() becomes a no-op for this variant. A new iteration_state_object_is_snapshot_independent_of_source test covers CoW against a mutated alias. Value::Set still wraps Rc<BTreeSet<Value>>; the matching Set abstraction and its swap ship in follow-up PRs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Builds on #57. Swap Value::Object's payload from Rc<BTreeMap<Value, Value>> to Rc<Object> and migrate all call sites to the Object API. as_object / as_object_mut keep their names but return &Object / &mut Object. The mutable accessor handles Rc::make_mut internally, so callers no longer do it themselves. Object grows into_value() and From<Object> for Value. Value's serializer now delegates to Object::serialize, dropping a duplicate non-string-key stringification path. RVM IterationState::Object is rewritten around ObjectCursor: O(log n) steps over a shared Rc<Object>, no eager pair snapshot. Snapshot independence is preserved by Rc copy-on-write; setup_next_iteration advances the cursor inline and advance() becomes a no-op for this variant. A new iteration_state_object_is_snapshot_independent_of_source test covers CoW against a mutated alias. Value::Set still wraps Rc<BTreeSet<Value>>; the matching Set abstraction and its swap ship in follow-up PRs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
First in a small series that introduces opaque storage types for
Valuevariants so future backends can swap in without rippling through call sites. The design also lets multiple backends coexist at runtime — differentObjectinstances in the same process can use different implementations (e.g. a lazy DB-backed object forinput, inline small-map objects for SARIF location records, regular sorted maps elsewhere) and interoperate through the same opaque type.The design and rationale are written up in
docs/value/object.md. It covers the encapsulation choice, the explicititervsiter_sortedsplit, the cursor for resumable traversal, whyOrdis defined against sorted iteration, and the concrete workloads driving the abstraction (SARIF small-object pressure, Kubernetes admission, Azure Policy aliases and case-insensitive compare, external/lazy data sources, eval-time arenas, host-language FFI). Precedents:serde_json::Map,toml::Table,simdjsonDOM.Layout: the new type lives at
src/value/object.rs;src/value.rsis moved tosrc/value/mod.rsto make room. Re-exported asregorus::Object.Follow-ups (separate PRs):