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

            
3
use tree_morph::prelude::*;
4
use uniplate::derive::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
        if let Shape::Triangle = **inner {
18
5
            return Some(Shape::Triangle);
19
4
        }
20
3
    }
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
        if let Shape::Circle(inner_inner) = inner.as_ref() {
28
1
            if let Shape::Triangle = **inner_inner {
29
1
                return Some(Shape::Square);
30
            }
31
        }
32
3
    }
33
3
    None
34
4
}
35

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

            
41
1
    let (result, _) = morph(
42
1
        vec![rule_fns![circ_tri_to_tri], rule_fns![circ_circ_tri_to_sqr]],
43
1
        select_first,
44
1
        expr,
45
1
        (),
46
1
    );
47
1

            
48
1
    assert_eq!(result, Shape::Triangle);
49
1
}
50

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

            
56
1
    // Same priority group - 2nd rule applies first as it applies higher in the tree
57
1
    let (result, _) = morph(
58
1
        vec![rule_fns![circ_tri_to_tri, circ_circ_tri_to_sqr]],
59
1
        select_first,
60
1
        expr,
61
1
        (),
62
1
    );
63
1

            
64
1
    assert_eq!(result, Shape::Square);
65
1
}
66

            
67
#[test]
68
1
fn shape_higher_priority() {
69
1
    // O(O(/\))
70
1
    let expr = Shape::Circle(Box::new(Shape::Circle(Box::new(Shape::Triangle))));
71
1

            
72
1
    // Higher priority group - 1st rule applies first even though it applies lower in the tree
73
1
    let (result, _) = morph(
74
1
        vec![rule_fns![circ_tri_to_tri], rule_fns![circ_circ_tri_to_sqr]],
75
1
        select_first,
76
1
        expr,
77
1
        (),
78
1
    );
79
1

            
80
1
    // O(O(/\)) ~> O(/\) ~> /\
81
1
    assert_eq!(result, Shape::Triangle);
82
1
}
83

            
84
#[should_panic]
85
#[test]
86
1
fn shape_multiple_rules_panic() {
87
1
    // O(O(/\))
88
1
    let expr = Shape::Circle(Box::new(Shape::Circle(Box::new(Shape::Triangle))));
89
1

            
90
1
    // Same rule twice, applicable at the same time
91
1
    morph(
92
1
        vec![rule_fns![circ_tri_to_tri, circ_tri_to_tri]],
93
1
        tree_morph::helpers::select_panic,
94
1
        expr,
95
1
        (),
96
1
    );
97
1
}