1
// Example case from the 05/02/2025 Conjure VIP meeting
2

            
3
use tree_morph::{helpers::select_panic, prelude::*};
4
use uniplate::Uniplate;
5

            
6
#[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
7
#[uniplate()]
8
enum Shape {
9
    Circle(Box<Shape>),
10
    Square,
11
    Triangle,
12
}
13

            
14
// O(/\) ~> /\
15
12
fn circ_tri_to_tri(_: &mut Commands<Shape, ()>, expr: &Shape, _: &()) -> Option<Shape> {
16
12
    if let Shape::Circle(inner) = expr
17
9
        && let Shape::Triangle = **inner
18
    {
19
5
        return Some(Shape::Triangle);
20
7
    }
21
7
    None
22
12
}
23

            
24
// O(O(/\)) ~> []
25
4
fn circ_circ_tri_to_sqr(_: &mut Commands<Shape, ()>, expr: &Shape, _: &()) -> Option<Shape> {
26
4
    if let Shape::Circle(inner) = expr
27
1
        && let Shape::Circle(inner_inner) = inner.as_ref()
28
1
        && let Shape::Triangle = **inner_inner
29
    {
30
1
        return Some(Shape::Square);
31
3
    }
32
3
    None
33
4
}
34

            
35
#[test]
36
1
fn circ_tri() {
37
    // O(/\)
38
1
    let expr = Shape::Circle(Box::new(Shape::Triangle));
39

            
40
1
    let engine = EngineBuilder::new()
41
1
        .add_rule(circ_tri_to_tri as RuleFn<_, _>)
42
1
        .add_rule(circ_circ_tri_to_sqr as RuleFn<_, _>)
43
1
        .build();
44
1
    let (result, _) = engine.morph(expr, ());
45

            
46
1
    assert_eq!(result, Shape::Triangle);
47
1
}
48

            
49
#[test]
50
1
fn circ_circ_tri() {
51
    // O(O(/\))
52
1
    let expr = Shape::Circle(Box::new(Shape::Circle(Box::new(Shape::Triangle))));
53

            
54
    // Same priority group - 2nd rule applies first as it applies higher in the tree
55
1
    let engine = EngineBuilder::new()
56
1
        .add_rule_group(rule_fns![circ_tri_to_tri, circ_circ_tri_to_sqr])
57
1
        .build();
58
1
    let (result, _) = engine.morph(expr, ());
59

            
60
1
    assert_eq!(result, Shape::Square);
61
1
}
62

            
63
#[test]
64
1
fn shape_higher_priority() {
65
    // O(O(/\))
66
1
    let expr = Shape::Circle(Box::new(Shape::Circle(Box::new(Shape::Triangle))));
67

            
68
    // Higher priority group - 1st rule applies first even though it applies lower in the tree
69
1
    let engine = EngineBuilder::new()
70
1
        .add_rule(circ_tri_to_tri as RuleFn<_, _>)
71
1
        .add_rule(circ_circ_tri_to_sqr as RuleFn<_, _>)
72
1
        .build();
73
1
    let (result, _) = engine.morph(expr, ());
74

            
75
    // O(O(/\)) ~> O(/\) ~> /\
76
1
    assert_eq!(result, Shape::Triangle);
77
1
}
78

            
79
#[should_panic]
80
#[test]
81
1
fn shape_multiple_rules_panic() {
82
    // O(O(/\))
83
1
    let expr = Shape::Circle(Box::new(Shape::Circle(Box::new(Shape::Triangle))));
84

            
85
    // Same rule twice, applicable at the same time
86
1
    let engine = EngineBuilder::new()
87
1
        .set_selector(select_panic)
88
1
        .add_rule_group(rule_fns![circ_tri_to_tri, circ_tri_to_tri])
89
1
        .build();
90
1
    engine.morph(expr, ());
91
1
}