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(
59            Arc::new(NoopEvalPolicy),
60            Arc::new(DefaultFactory),
61            sim_kernel::HandleSeed::new(0x5348_5101),
62        )
63    }
64
65    #[test]
66    fn directional_shape_queries_match_relation_semantics() {
67        let mut cx = bare_cx();
68        let any = AnyShape;
69        let string = ExprKindShape::new(ExprKind::String);
70
71        assert!(shape_query_matches(&mut cx, &any, &string, ShapeQueryRelation::Subsumes).unwrap());
72        assert!(
73            !shape_query_matches(&mut cx, &string, &any, ShapeQueryRelation::Subsumes).unwrap()
74        );
75        assert!(
76            shape_query_matches(&mut cx, &string, &any, ShapeQueryRelation::SubshapeOf).unwrap()
77        );
78        assert!(shape_query_matches(&mut cx, &string, &any, ShapeQueryRelation::Overlaps).unwrap());
79    }
80
81    #[test]
82    fn disjoint_shapes_do_not_overlap() {
83        let mut cx = bare_cx();
84        let string = ExprKindShape::new(ExprKind::String);
85        let number = ExprKindShape::new(ExprKind::Number);
86
87        assert!(
88            !shape_query_matches(&mut cx, &string, &number, ShapeQueryRelation::Overlaps).unwrap()
89        );
90    }
91}