Skip to main content

sim_shape/
query.rs

1//! Reusable shape-query predicates built on relation analysis.
2
3use sim_kernel::{Cx, Result};
4
5use crate::base::Shape;
6use crate::compare::{ShapeRelationKind, relate_shapes};
7
8/// Direction used when matching a candidate Shape against a requested Shape.
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum ShapeQueryRelation {
11    /// The candidate accepts every value accepted by the requested Shape.
12    Subsumes,
13    /// The candidate is contained by the requested Shape.
14    SubshapeOf,
15    /// The candidate and requested Shapes have at least one known value in common.
16    Overlaps,
17}
18
19/// Returns whether `candidate` satisfies the requested relation to `wanted`.
20///
21/// The relation is computed with the existing [`relate_shapes`] helper. Unknown
22/// relations fail closed for query filtering.
23pub fn shape_query_matches(
24    cx: &mut Cx,
25    candidate: &dyn Shape,
26    wanted: &dyn Shape,
27    relation: ShapeQueryRelation,
28) -> Result<bool> {
29    let relation_kind = relate_shapes(cx, candidate, wanted, &[])?.kind;
30    Ok(match relation {
31        ShapeQueryRelation::Subsumes => matches!(
32            relation_kind,
33            ShapeRelationKind::Equal | ShapeRelationKind::RightSubshape
34        ),
35        ShapeQueryRelation::SubshapeOf => matches!(
36            relation_kind,
37            ShapeRelationKind::Equal | ShapeRelationKind::LeftSubshape
38        ),
39        ShapeQueryRelation::Overlaps => matches!(
40            relation_kind,
41            ShapeRelationKind::Equal
42                | ShapeRelationKind::LeftSubshape
43                | ShapeRelationKind::RightSubshape
44                | ShapeRelationKind::Overlap
45        ),
46    })
47}
48
49#[cfg(test)]
50mod tests {
51    use std::sync::Arc;
52
53    use sim_kernel::{Cx, DefaultFactory, NoopEvalPolicy};
54
55    use crate::{AnyShape, ExprKind, ExprKindShape, ShapeQueryRelation, shape_query_matches};
56
57    fn bare_cx() -> Cx {
58        Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory))
59    }
60
61    #[test]
62    fn directional_shape_queries_match_relation_semantics() {
63        let mut cx = bare_cx();
64        let any = AnyShape;
65        let string = ExprKindShape::new(ExprKind::String);
66
67        assert!(shape_query_matches(&mut cx, &any, &string, ShapeQueryRelation::Subsumes).unwrap());
68        assert!(
69            !shape_query_matches(&mut cx, &string, &any, ShapeQueryRelation::Subsumes).unwrap()
70        );
71        assert!(
72            shape_query_matches(&mut cx, &string, &any, ShapeQueryRelation::SubshapeOf).unwrap()
73        );
74        assert!(shape_query_matches(&mut cx, &string, &any, ShapeQueryRelation::Overlaps).unwrap());
75    }
76
77    #[test]
78    fn disjoint_shapes_do_not_overlap() {
79        let mut cx = bare_cx();
80        let string = ExprKindShape::new(ExprKind::String);
81        let number = ExprKindShape::new(ExprKind::Number);
82
83        assert!(
84            !shape_query_matches(&mut cx, &string, &number, ShapeQueryRelation::Overlaps).unwrap()
85        );
86    }
87}