1
use itertools::Itertools;
2

            
3
use crate::set_automaton::SetAutomaton;
4
use crate::set_automaton::State;
5
use core::fmt;
6

            
7
use super::MatchAnnouncement;
8
use super::MatchObligation;
9
use super::Transition;
10

            
11
impl<M> fmt::Debug for Transition<M> {
12
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13
        write!(
14
            f,
15
            "Transition {{ {}, announce: [{:?}], dest: [{:?}] }}",
16
            self.symbol,
17
            self.announcements.iter().map(|(x, _)| { x }).format(", "),
18
            self.destinations.iter().format_with(", ", |element, f| {
19
                f(&format_args!("{} -> {}", element.0, element.1))
20
            })
21
        )
22
    }
23
}
24

            
25
impl fmt::Debug for MatchAnnouncement {
26
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27
        write!(f, "({})@{}", self.rule, self.position)
28
    }
29
}
30

            
31
impl fmt::Debug for MatchObligation {
32
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33
        write!(f, "{}@{}", self.pattern, self.position)
34
    }
35
}
36

            
37
impl fmt::Debug for State {
38
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39
        writeln!(f, "Label: {}, ", self.label())?;
40
        writeln!(f, "Match goals: [")?;
41
        for m in self.match_goals() {
42
            writeln!(f, "\t {m:?}")?;
43
        }
44
        write!(f, "]")
45
    }
46
}
47

            
48
impl<M> fmt::Debug for SetAutomaton<M> {
49
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50
        writeln!(f, "States: {{")?;
51

            
52
        for (state_index, s) in self.states().iter().enumerate() {
53
            writeln!(f, "State {state_index} {{\n{s:?}")?;
54

            
55
            writeln!(f, "Transitions: {{")?;
56
            for ((from, _), tr) in self.transitions() {
57
                if state_index == *from {
58
                    writeln!(f, "\t {tr:?}")?;
59
                }
60
            }
61
            writeln!(f, "}}")?;
62
        }
63

            
64
        writeln!(f, "}}")
65
    }
66
}
67

            
68
pub struct DotFormatter<'a, M> {
69
    pub(crate) automaton: &'a SetAutomaton<M>,
70
    pub(crate) show_backtransitions: bool,
71
    pub(crate) show_final: bool,
72
}
73

            
74
impl<M> fmt::Display for DotFormatter<'_, M> {
75
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76
        // Write the header anf final states.
77
        writeln!(f, "digraph{{")?;
78

            
79
        if self.show_final {
80
            writeln!(f, "  final[label=\"💩\"];")?;
81
        }
82

            
83
        for (i, s) in self.automaton.states().iter().enumerate() {
84
            let match_goals = s.match_goals().iter().format_with("\\n", |goal, f| {
85
                f(&format_args!("{}", html_escape::encode_safe(&format!("{goal:?}"))))
86
            });
87

            
88
            writeln!(
89
                f,
90
                "  s{}[shape=record label=\"{{{{s{} | {}}} | {}}}\"]",
91
                i,
92
                i,
93
                s.label(),
94
                match_goals
95
            )?;
96
        }
97

            
98
        for ((i, _), tr) in self.automaton.transitions() {
99
            let announcements = tr.announcements.iter().format_with(", ", |(announcement, _), f| {
100
                f(&format_args!("{}@{}", announcement.rule.rhs, announcement.position))
101
            });
102

            
103
            if tr.destinations.is_empty() {
104
                if self.show_final {
105
                    writeln!(f, "  s{} -> final [label=\"{} \\[{}\\]\"]", i, tr.symbol, announcements)?;
106
                }
107
            } else {
108
                writeln!(f, "  \"s{}{}\" [shape=point]", i, tr.symbol,).unwrap();
109
                writeln!(
110
                    f,
111
                    "  s{} -> \"s{}{}\" [label=\"{} \\[{}\\]\"]",
112
                    i, i, tr.symbol, tr.symbol, announcements
113
                )?;
114

            
115
                for (pos, des) in &tr.destinations {
116
                    if self.show_backtransitions || *des != 0 {
117
                        // Hide backpointers to the initial state.
118
                        writeln!(f, "  \"s{}{}\" -> s{} [label = \"{}\"]", i, tr.symbol, des, pos)?;
119
                    }
120
                }
121
            }
122
        }
123
        writeln!(f, "}}")
124
    }
125
}