toasty_core/stmt/
hash_index.rs

1use super::{Entry, Projection, Value};
2
3use std::collections::HashMap;
4
5/// A unique hash index over a borrowed slice of [`Value`]s.
6///
7/// Keys are extracted from each value using a set of [`Projection`]s. The key is
8/// the composite of the projected field values. Only equality lookup is supported.
9///
10/// Both construction and lookup are O(1) amortized (hash map operations).
11///
12/// # Uniqueness
13///
14/// The index assumes each extracted key is unique across the source slice. A
15/// `debug_assert!` fires on duplicate keys at build time.
16///
17/// # Cloning
18///
19/// Key fields are cloned into owned [`Value`]s for use as map keys. Full records
20/// are never cloned — the map values are `&'a Value` references into the source slice.
21pub struct HashIndex<'a> {
22    map: HashMap<Vec<Value>, &'a Value>,
23}
24
25impl<'a> HashIndex<'a> {
26    /// Build an index over `values`, keyed by the fields selected by `projections`.
27    ///
28    /// Each projection navigates into a value to extract one key component. Multiple
29    /// projections produce a composite key compared lexicographically.
30    pub fn new(values: &'a [Value], projections: &[Projection]) -> Self {
31        let mut map = HashMap::with_capacity(values.len());
32
33        for value in values {
34            let key = extract_key(value, projections);
35            let prev = map.insert(key, value);
36            debug_assert!(prev.is_none(), "HashIndex: duplicate key detected");
37        }
38
39        Self { map }
40    }
41
42    /// Look up the value whose key equals `key`.
43    ///
44    /// `key` must be a slice of values with one entry per projection used at build time.
45    /// Returns `None` if no value matches.
46    pub fn find(&self, key: &[Value]) -> Option<&'a Value> {
47        self.map.get(key).copied()
48    }
49}
50
51/// Extract the composite key from `value` using `projections`.
52///
53/// Each projection is applied to `value` in sequence, collecting the resulting
54/// field references into an owned `Vec<Value>`.
55fn extract_key(value: &Value, projections: &[Projection]) -> Vec<Value> {
56    projections
57        .iter()
58        .map(|proj| match value.entry(proj) {
59            Entry::Value(v) => v.clone(),
60            Entry::Expr(_) => panic!("projection yielded an expression, not a value"),
61        })
62        .collect()
63}