Skip to main content

vantage_dataset/im/
mod.rs

1// src/im/mod.rs
2
3use indexmap::IndexMap;
4use std::collections::HashMap;
5use std::sync::{Arc, Mutex};
6use vantage_types::Record;
7
8pub mod dataset_insertable;
9pub mod dataset_readable;
10pub mod dataset_writable;
11pub mod im_table;
12
13pub mod valueset_insertable;
14pub mod valueset_readable;
15pub mod valueset_writable;
16pub use im_table::ImTable;
17
18/// Type alias for the complex table storage structure
19type TableStorage<V> = Arc<Mutex<HashMap<String, IndexMap<String, Record<V>>>>>;
20
21/// In-memory data source storing tables as nested maps, keyed first by table
22/// name then by row id. Generic over the wire value type `V` so the same
23/// storage primitive can hold `serde_json::Value` records (the original
24/// entity-friendly mode) or `ciborium::Value` records (used by
25/// `MockTableSource` to participate in the CBOR-typed `TableSource` /
26/// `Vista` machinery). Defaults to `serde_json::Value` for back-compat.
27#[derive(Debug)]
28pub struct ImDataSource<V = serde_json::Value> {
29    // table_name -> IndexMap<id, record>
30    tables: TableStorage<V>,
31}
32
33impl<V> Clone for ImDataSource<V> {
34    fn clone(&self) -> Self {
35        Self {
36            tables: self.tables.clone(),
37        }
38    }
39}
40
41impl<V> ImDataSource<V> {
42    pub fn new() -> Self {
43        Self {
44            tables: Arc::new(Mutex::new(HashMap::new())),
45        }
46    }
47}
48
49impl<V: Clone> ImDataSource<V> {
50    /// Run `f` against an immutable view of the named table, holding the lock
51    /// for its duration. Missing tables present as empty (without being
52    /// created). Clone only what you need to return — the borrow ends with `f`.
53    pub(super) fn with_table<R>(
54        &self,
55        table_name: &str,
56        f: impl FnOnce(&IndexMap<String, Record<V>>) -> R,
57    ) -> R {
58        let tables = self.tables.lock().unwrap();
59        match tables.get(table_name) {
60            Some(table) => f(table),
61            None => f(&IndexMap::new()),
62        }
63    }
64
65    /// Number of rows currently stored in the named table (`0` if it has never
66    /// been written to). Synchronous and clone-free — reads the row count under
67    /// the storage lock. Lets sync callers (e.g. `MockTableSource`) derive a
68    /// count from the single source of truth instead of a side store.
69    pub fn table_len(&self, table_name: &str) -> usize {
70        self.with_table(table_name, |table| table.len())
71    }
72
73    /// Run `f` against a mutable view of the named table (created on demand),
74    /// holding the lock across the whole read-modify-write so concurrent
75    /// writers can't clobber each other's changes.
76    pub(super) fn with_table_mut<R>(
77        &self,
78        table_name: &str,
79        f: impl FnOnce(&mut IndexMap<String, Record<V>>) -> R,
80    ) -> R {
81        let mut tables = self.tables.lock().unwrap();
82        let table = tables.entry(table_name.to_string()).or_default();
83        f(table)
84    }
85}
86
87impl<V> Default for ImDataSource<V> {
88    fn default() -> Self {
89        Self::new()
90    }
91}