essential_debugger/
source.rs

1use std::fmt::Write;
2
3#[cfg(test)]
4mod tests;
5
6#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct Source {
8    pub other: String,
9    pub predicate: String,
10    pub constraint_line: Option<usize>,
11}
12
13#[derive(Default, Debug, Clone, Copy)]
14pub enum ShowOutput {
15    All,
16    Predicate,
17    #[default]
18    Constraint,
19    ConstraintOnly,
20}
21
22pub fn show_code(source: &Option<Source>, show: ShowOutput) -> String {
23    match source {
24        Some(source) => match show {
25            ShowOutput::All => format!(
26                "{}\n{}",
27                source.other,
28                format_predicate(&source.predicate, &source.constraint_line)
29            ),
30            ShowOutput::Predicate => format_predicate(&source.predicate, &source.constraint_line),
31            ShowOutput::Constraint => format_constraint(&source.predicate, &source.constraint_line),
32            ShowOutput::ConstraintOnly => {
33                constraint_only(&source.predicate, &source.constraint_line)
34            }
35        },
36        None => "No source code available.".to_string(),
37    }
38}
39
40impl Source {
41    pub fn with_predicate(self, predicate: impl Into<String>) -> Self {
42        Source {
43            predicate: predicate.into(),
44            ..self
45        }
46    }
47
48    pub fn with_predicate_find_line(
49        self,
50        predicate: impl Into<String>,
51        constraint_num: usize,
52    ) -> Self {
53        let predicate = predicate.into();
54        let mut count = 0;
55        let constraint_line = predicate.lines().position(|line| {
56            if line.trim().starts_with("constraint ") {
57                let found = count == constraint_num;
58                count += 1;
59                found
60            } else {
61                false
62            }
63        });
64        Source {
65            predicate,
66            constraint_line,
67            ..self
68        }
69    }
70
71    pub fn with_constraint_line_number(self, constraint_line: usize) -> Self {
72        Source {
73            constraint_line: Some(constraint_line),
74            ..self
75        }
76    }
77
78    pub fn with_other_code(self, other: impl Into<String>) -> Self {
79        Source {
80            other: other.into(),
81            ..self
82        }
83    }
84}
85
86fn format_predicate(predicate: &str, constraint_line: &Option<usize>) -> String {
87    match constraint_line {
88        Some(line_num) => predicate.lines().enumerate().fold(
89            String::with_capacity(predicate.len()),
90            |mut s, (i, line)| {
91                if i == *line_num {
92                    let _ = writeln!(s, "{}", dialoguer::console::style(line).cyan());
93                } else {
94                    let _ = writeln!(s, "{}", line);
95                }
96                s
97            },
98        ),
99        None => predicate.to_string(),
100    }
101}
102
103fn format_constraint(predicate: &str, constraint_line: &Option<usize>) -> String {
104    match constraint_line {
105        Some(line_num) => predicate.lines().enumerate().fold(
106            String::with_capacity(predicate.len()),
107            |mut s, (i, line)| {
108                if i == *line_num {
109                    let _ = writeln!(s, "{}", line);
110                } else if line.trim().starts_with("constraint ") {
111                } else {
112                    let _ = writeln!(s, "{}", line);
113                }
114                s
115            },
116        ),
117        None => predicate.to_string(),
118    }
119}
120
121fn constraint_only(predicate: &str, constraint_line: &Option<usize>) -> String {
122    match constraint_line {
123        Some(line_num) => match predicate.lines().nth(*line_num) {
124            Some(line) => line.trim().to_string(),
125            None => predicate.to_string(),
126        },
127        None => predicate.to_string(),
128    }
129}
130
131impl From<Option<&str>> for ShowOutput {
132    fn from(value: Option<&str>) -> Self {
133        match value {
134            Some("a") | Some("all") => ShowOutput::All,
135            Some("p") | Some("predicate") => ShowOutput::Predicate,
136            Some("c") | Some("constraint") => ShowOutput::Constraint,
137            Some("co") | Some("constraint only") => ShowOutput::ConstraintOnly,
138            _ => ShowOutput::default(),
139        }
140    }
141}