Skip to main content

panopticon_core/data/
entry.rs

1use crate::imports::*;
2
3/// The generic entry type held by the runtime [`Store`].
4///
5/// A `StoreEntry` is one of three shapes: a scalar [`Var`](Self::Var)
6/// pairing a [`Value`] with its [`Type`], an [`Array`](Self::Array) of
7/// nested entries, or a [`Map`](Self::Map) of named nested entries. This
8/// union is what lets the store hold variables, collection handles, step
9/// outputs, and resolved parameters uniformly.
10///
11/// The `get_*` methods (taking a key or index) traverse collection
12/// entries and fail with an [`AccessError`] when the entry has the wrong
13/// shape. The `as_*` methods narrow an entry to a specific variant for
14/// callers that know what they are holding.
15#[derive(Debug, Clone, PartialEq)]
16pub enum StoreEntry {
17    /// A scalar value paired with its type tag.
18    Var {
19        /// The stored value.
20        value: Value,
21        /// The type tag derived from `value` at insertion time.
22        ty: Type,
23    },
24    /// An ordered array of nested entries.
25    Array(Vec<StoreEntry>),
26    /// A map of named nested entries.
27    Map(HashMap<String, StoreEntry>),
28}
29
30impl StoreEntry {
31    /// Looks up a child entry by key. Fails with
32    /// [`AccessError::NotAMap`] if this entry is not a [`Map`](Self::Map),
33    /// or [`AccessError::NotFound`] if the key is missing.
34    pub fn get_key(&self, key: &str) -> Result<&StoreEntry, AccessError> {
35        match self {
36            StoreEntry::Var { .. } => Err(AccessError::NotAMap("Var")),
37            StoreEntry::Array(_) => Err(AccessError::NotAMap("Array")),
38            StoreEntry::Map(entries) => entries
39                .get(key)
40                .ok_or_else(|| AccessError::NotFound(key.into())),
41        }
42    }
43    /// Looks up a child entry by index. Fails with
44    /// [`AccessError::NotAnArray`] if this entry is not an
45    /// [`Array`](Self::Array), or [`AccessError::IndexOutOfBounds`] if
46    /// the index is out of range.
47    pub fn get_index(&self, index: usize) -> Result<&StoreEntry, AccessError> {
48        match self {
49            StoreEntry::Var { .. } => Err(AccessError::NotAnArray("Var")),
50            StoreEntry::Array(items) => {
51                items.get(index).ok_or(AccessError::IndexOutOfBounds(index))
52            }
53            StoreEntry::Map(_) => Err(AccessError::NotAnArray("Map")),
54        }
55    }
56    /// Returns the inner [`Value`] of a [`Var`](Self::Var) entry. Fails
57    /// with [`AccessError::NotAVar`] for arrays or maps.
58    pub fn get_value(&self) -> Result<&Value, AccessError> {
59        match self {
60            StoreEntry::Var { value, .. } => Ok(value),
61            StoreEntry::Array(_) => Err(AccessError::NotAVar("Array")),
62            StoreEntry::Map(_) => Err(AccessError::NotAVar("Map")),
63        }
64    }
65    /// Narrows this entry to a [`Var`](Self::Var), returning its value
66    /// and type. Fails with [`AccessError::NotAVar`] for arrays or maps.
67    pub fn as_var(&self) -> Result<(&Value, &Type), AccessError> {
68        match self {
69            StoreEntry::Var { value, ty } => Ok((value, ty)),
70            StoreEntry::Array(_) => Err(AccessError::NotAVar("Array")),
71            StoreEntry::Map(_) => Err(AccessError::NotAVar("Map")),
72        }
73    }
74    /// Narrows this entry to an [`Array`](Self::Array), returning the
75    /// underlying vector. Fails with [`AccessError::NotAnArray`] for
76    /// vars or maps.
77    pub fn as_array(&self) -> Result<&Vec<StoreEntry>, AccessError> {
78        match self {
79            StoreEntry::Var { .. } => Err(AccessError::NotAnArray("Var")),
80            StoreEntry::Array(items) => Ok(items),
81            StoreEntry::Map(_) => Err(AccessError::NotAnArray("Map")),
82        }
83    }
84    /// Narrows this entry to a [`Map`](Self::Map), returning the
85    /// underlying hash map. Fails with [`AccessError::NotAMap`] for vars
86    /// or arrays.
87    pub fn as_map(&self) -> Result<&HashMap<String, StoreEntry>, AccessError> {
88        match self {
89            StoreEntry::Var { .. } => Err(AccessError::NotAMap("Var")),
90            StoreEntry::Array(_) => Err(AccessError::NotAMap("Array")),
91            StoreEntry::Map(entries) => Ok(entries),
92        }
93    }
94}
95
96impl std::cmp::Eq for StoreEntry {}
97
98impl std::hash::Hash for StoreEntry {
99    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
100        std::mem::discriminant(self).hash(state);
101        match self {
102            StoreEntry::Var { value, ty } => {
103                value.hash(state);
104                ty.hash(state);
105            }
106            StoreEntry::Array(items) => items.hash(state),
107            StoreEntry::Map(map) => {
108                let mut keys: Vec<_> = map.keys().collect();
109                keys.sort();
110                for key in keys {
111                    key.hash(state);
112                    map[key].hash(state);
113                }
114            }
115        }
116    }
117}
118
119impl<T: Into<Value>> From<T> for StoreEntry {
120    fn from(v: T) -> Self {
121        let value = v.into();
122        StoreEntry::Var {
123            ty: value.get_type(),
124            value,
125        }
126    }
127}
128
129impl From<&Value> for StoreEntry {
130    fn from(v: &Value) -> Self {
131        let value = v.clone();
132        StoreEntry::Var {
133            ty: value.get_type(),
134            value,
135        }
136    }
137}
138
139impl From<Vec<StoreEntry>> for StoreEntry {
140    fn from(items: Vec<StoreEntry>) -> Self {
141        StoreEntry::Array(items)
142    }
143}