Skip to main content

triblespace_core/query/
hashmapconstraint.rs

1use std::collections::HashMap;
2use std::ops::Deref;
3use std::rc::Rc;
4use std::sync::Arc;
5
6use crate::query::Binding;
7use crate::query::Constraint;
8use crate::query::ContainsConstraint;
9use crate::query::Variable;
10use crate::query::VariableId;
11use crate::query::VariableSet;
12use crate::inline::RawInline;
13use crate::inline::IntoInline;
14use crate::inline::TryFromInline;
15use crate::inline::Inline;
16use crate::inline::InlineEncoding;
17
18/// Constrains a variable to keys present in a [`HashMap`].
19///
20/// Created via the [`ContainsConstraint`]
21/// trait (`.has(variable)`). Proposals enumerate every key in the map;
22/// confirmations retain only proposals whose key exists. Accepts
23/// `&HashMap<K,V>`, `Rc<HashMap<K,V>>`, and `Arc<HashMap<K,V>>`.
24pub struct KeysConstraint<S: InlineEncoding, R, K, V>
25where
26    R: Deref<Target = HashMap<K, V>>,
27{
28    variable: Variable<S>,
29    map: R,
30}
31
32impl<S: InlineEncoding, R, K, V> KeysConstraint<S, R, K, V>
33where
34    R: Deref<Target = HashMap<K, V>>,
35{
36    /// Creates a constraint that restricts `variable` to keys in `map`.
37    pub fn new(variable: Variable<S>, map: R) -> Self {
38        KeysConstraint { variable, map }
39    }
40}
41
42impl<'a, S: InlineEncoding, R, K, V> Constraint<'a> for KeysConstraint<S, R, K, V>
43where
44    K: 'a + std::cmp::Eq + std::hash::Hash + for<'b> TryFromInline<'b, S>,
45    for<'b> &'b K: IntoInline<S>,
46    V: 'a,
47    R: Deref<Target = HashMap<K, V>>,
48{
49    fn variables(&self) -> VariableSet {
50        VariableSet::new_singleton(self.variable.index)
51    }
52
53    fn estimate(&self, variable: VariableId, _binding: &Binding) -> Option<usize> {
54        if self.variable.index == variable {
55            // the estimated proposal count equals the current number of keys
56            Some(self.map.len())
57        } else {
58            None
59        }
60    }
61
62    fn propose(&self, variable: VariableId, _binding: &Binding, proposals: &mut Vec<RawInline>) {
63        if self.variable.index == variable {
64            proposals.extend(self.map.keys().map(|k| IntoInline::to_inline(k).raw));
65        }
66    }
67
68    fn confirm(&self, variable: VariableId, _binding: &Binding, proposals: &mut Vec<RawInline>) {
69        if self.variable.index == variable {
70            proposals.retain(|v| {
71                self.map.contains_key(&match TryFromInline::try_from_inline(
72                    Inline::<S>::as_transmute_raw(v),
73                ) {
74                    Ok(v) => v,
75                    Err(_) => return false,
76                })
77            });
78        }
79    }
80}
81
82impl<'a, S: InlineEncoding, K, V> ContainsConstraint<'a, S> for &'a HashMap<K, V>
83where
84    K: 'a + std::cmp::Eq + std::hash::Hash + for<'b> TryFromInline<'b, S>,
85    for<'b> &'b K: IntoInline<S>,
86    V: 'a,
87{
88    type Constraint = KeysConstraint<S, Self, K, V>;
89
90    fn has(self, v: Variable<S>) -> Self::Constraint {
91        KeysConstraint::new(v, self)
92    }
93}
94
95impl<'a, S: InlineEncoding, K, V> ContainsConstraint<'a, S> for Rc<HashMap<K, V>>
96where
97    K: 'a + std::cmp::Eq + std::hash::Hash + for<'b> TryFromInline<'b, S>,
98    for<'b> &'b K: IntoInline<S>,
99    V: 'a,
100{
101    type Constraint = KeysConstraint<S, Self, K, V>;
102
103    fn has(self, v: Variable<S>) -> Self::Constraint {
104        KeysConstraint::new(v, self)
105    }
106}
107
108impl<'a, S: InlineEncoding, K, V> ContainsConstraint<'a, S> for Arc<HashMap<K, V>>
109where
110    K: 'a + std::cmp::Eq + std::hash::Hash + for<'b> TryFromInline<'b, S>,
111    for<'b> &'b K: IntoInline<S>,
112    V: 'a,
113{
114    type Constraint = KeysConstraint<S, Self, K, V>;
115
116    fn has(self, v: Variable<S>) -> Self::Constraint {
117        KeysConstraint::new(v, self)
118    }
119}