feat(model): use IndexMap for deterministic insertion order in DataStore

Replace `HashMap` with `IndexMap` in `DataStore::cells` to preserve
insertion order. This allows records mode to display rows in the order they
were added. Update `remove` to use `shift_remove` to maintain order during
deletions.

Co-Authored-By: fiddlerwoaroof/git-smart-commit (gemma-4-26B-A4B-it-UD-Q5_K_XL.gguf)
This commit is contained in:
Edward Langley
2026-04-15 21:32:35 -07:00
parent 7c00695398
commit ded35f705c

View File

@ -1,3 +1,4 @@
use indexmap::IndexMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -104,8 +105,9 @@ pub struct InternedKey(pub Vec<(Symbol, Symbol)>);
/// to implement the `Serialize`-as-string requirement for JSON object keys. /// to implement the `Serialize`-as-string requirement for JSON object keys.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct DataStore { pub struct DataStore {
/// Primary storage — interned keys for O(1) hash/compare. /// Primary storage — interned keys, insertion-ordered so records mode
cells: HashMap<InternedKey, CellValue>, /// can display rows in the order they were entered.
cells: IndexMap<InternedKey, CellValue>,
/// String interner — all category/item names are interned here. /// String interner — all category/item names are interned here.
pub symbols: SymbolTable, pub symbols: SymbolTable,
/// Secondary index: interned (category, item) → set of interned keys. /// Secondary index: interned (category, item) → set of interned keys.
@ -193,7 +195,7 @@ impl DataStore {
let Some(ikey) = self.lookup_key(key) else { let Some(ikey) = self.lookup_key(key) else {
return; return;
}; };
if self.cells.remove(&ikey).is_some() { if self.cells.shift_remove(&ikey).is_some() {
for pair in &ikey.0 { for pair in &ikey.0 {
if let Some(set) = self.index.get_mut(pair) { if let Some(set) = self.index.get_mut(pair) {
set.remove(&ikey); set.remove(&ikey);