Skip to main content

triblespace_core/query/
hashsetconstraint.rs

1use std::collections::HashSet;
2use std::ops::Deref;
3use std::rc::Rc;
4use std::sync::Arc;
5
6use crate::value::TryFromValue;
7use crate::value::ToValue;
8
9use super::*;
10
11/// Constrains a variable to values present in a [`HashSet`].
12///
13/// Created via the [`ContainsConstraint`] trait (`.has(variable)`).
14/// Proposals enumerate every element in the set; confirmations retain
15/// only proposals that the set contains. Accepts `&HashSet<T>`,
16/// `Rc<HashSet<T>>`, and `Arc<HashSet<T>>` as the backing store.
17pub struct SetConstraint<S: ValueSchema, R, T>
18where
19    R: Deref<Target = HashSet<T>>,
20{
21    variable: Variable<S>,
22    set: R,
23}
24
25impl<S: ValueSchema, R, T> SetConstraint<S, R, T>
26where
27    R: Deref<Target = HashSet<T>>,
28{
29    /// Creates a constraint that restricts `variable` to values in `set`.
30    pub fn new(variable: Variable<S>, set: R) -> Self {
31        SetConstraint { variable, set }
32    }
33}
34
35impl<'a, S: ValueSchema, R, T> Constraint<'a> for SetConstraint<S, R, T>
36where
37    T: 'a + std::cmp::Eq + std::hash::Hash + for<'b> TryFromValue<'b, S>,
38    for<'b> &'b T: ToValue<S>,
39    R: Deref<Target = HashSet<T>>,
40{
41    fn variables(&self) -> VariableSet {
42        VariableSet::new_singleton(self.variable.index)
43    }
44
45    fn estimate(&self, variable: VariableId, _binding: &Binding) -> Option<usize> {
46        if self.variable.index == variable {
47            // use the current set length as the estimate for proposal count
48            Some(self.set.len())
49        } else {
50            None
51        }
52    }
53
54    fn propose(&self, variable: VariableId, _binding: &Binding, proposals: &mut Vec<RawValue>) {
55        if self.variable.index == variable {
56            proposals.extend(self.set.iter().map(|v| ToValue::to_value(v).raw));
57        }
58    }
59
60    fn confirm(&self, variable: VariableId, _binding: &Binding, proposals: &mut Vec<RawValue>) {
61        if self.variable.index == variable {
62            proposals.retain(|v| {
63                match TryFromValue::try_from_value(Value::<S>::as_transmute_raw(v)) {
64                    Ok(t) => self.set.contains(&t),
65                    Err(_) => false,
66                }
67            });
68        }
69    }
70}
71
72impl<'a, S: ValueSchema, T> ContainsConstraint<'a, S> for &'a HashSet<T>
73where
74    T: 'a + std::cmp::Eq + std::hash::Hash + for<'b> TryFromValue<'b, S>,
75    for<'b> &'b T: ToValue<S>,
76{
77    type Constraint = SetConstraint<S, Self, T>;
78
79    fn has(self, v: Variable<S>) -> Self::Constraint {
80        SetConstraint::new(v, self)
81    }
82}
83
84impl<'a, S: ValueSchema, T> ContainsConstraint<'a, S> for Rc<HashSet<T>>
85where
86    T: 'a + std::cmp::Eq + std::hash::Hash + for<'b> TryFromValue<'b, S>,
87    for<'b> &'b T: ToValue<S>,
88{
89    type Constraint = SetConstraint<S, Self, T>;
90
91    fn has(self, v: Variable<S>) -> Self::Constraint {
92        SetConstraint::new(v, self)
93    }
94}
95
96impl<'a, S: ValueSchema, T> ContainsConstraint<'a, S> for Arc<HashSet<T>>
97where
98    T: 'a + std::cmp::Eq + std::hash::Hash + for<'b> TryFromValue<'b, S>,
99    for<'b> &'b T: ToValue<S>,
100{
101    type Constraint = SetConstraint<S, Self, T>;
102
103    fn has(self, v: Variable<S>) -> Self::Constraint {
104        SetConstraint::new(v, self)
105    }
106}