miden_debug/debug/
breakpoint.rs1use std::{ops::Deref, path::Path, str::FromStr};
2
3use glob::Pattern;
4
5use super::ResolvedLocation;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Breakpoint {
9 pub id: u8,
10 pub creation_cycle: usize,
11 pub ty: BreakpointType,
12}
13
14impl Default for Breakpoint {
15 fn default() -> Self {
16 Self {
17 id: 0,
18 creation_cycle: 0,
19 ty: BreakpointType::Step,
20 }
21 }
22}
23
24impl Breakpoint {
25 pub fn cycles_to_skip(&self, current_cycle: usize) -> Option<usize> {
29 let cycles_passed = current_cycle - self.creation_cycle;
30 match &self.ty {
31 BreakpointType::Step => Some(1),
32 BreakpointType::StepN(n) => Some(n.saturating_sub(cycles_passed)),
33 BreakpointType::StepTo(to) if to >= ¤t_cycle => Some(to.abs_diff(current_cycle)),
34 _ => None,
35 }
36 }
37}
38impl Deref for Breakpoint {
39 type Target = BreakpointType;
40
41 #[inline]
42 fn deref(&self) -> &Self::Target {
43 &self.ty
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum BreakpointType {
49 Step,
51 StepN(usize),
53 StepTo(usize),
55 Next,
57 Finish,
59 File(Pattern),
61 Line { pattern: Pattern, line: u32 },
64 #[allow(unused)]
66 Opcode(miden_core::operations::Operation),
67 Called(Pattern),
69}
70impl BreakpointType {
71 #[allow(unused)]
73 pub fn should_break_for(&self, current_op: &miden_core::operations::Operation) -> bool {
74 match self {
75 Self::Opcode(op) => current_op == op,
76 _ => false,
77 }
78 }
79
80 pub fn should_break_in(&self, procedure: &str) -> bool {
82 match self {
83 Self::Called(pattern) => pattern.matches(procedure),
84 _ => false,
85 }
86 }
87
88 pub fn should_break_at(&self, loc: &ResolvedLocation) -> bool {
90 match self {
91 Self::File(pattern) => {
92 pattern.matches_path(Path::new(loc.source_file.deref().content().uri().as_str()))
93 }
94 Self::Line { pattern, line } if line == &loc.line => {
95 pattern.matches_path(Path::new(loc.source_file.deref().content().uri().as_str()))
96 }
97 _ => false,
98 }
99 }
100
101 pub fn is_internal(&self) -> bool {
103 matches!(self, BreakpointType::Next | BreakpointType::Step | BreakpointType::Finish)
104 }
105
106 pub fn is_one_shot(&self) -> bool {
108 matches!(
109 self,
110 BreakpointType::Next
111 | BreakpointType::Finish
112 | BreakpointType::Step
113 | BreakpointType::StepN(_)
114 | BreakpointType::StepTo(_)
115 )
116 }
117}
118
119impl FromStr for BreakpointType {
120 type Err = String;
121
122 fn from_str(s: &str) -> Result<Self, Self::Err> {
123 let s = s.trim();
124
125 if s == "next" {
133 return Ok(BreakpointType::Next);
134 }
135 if s == "finish" {
136 return Ok(BreakpointType::Finish);
137 }
138 if let Some(n) = s.strip_prefix("after ") {
139 let n = n.trim().parse::<usize>().map_err(|err| {
140 format!("invalid breakpoint expression: could not parse cycle count: {err}")
141 })?;
142 return Ok(BreakpointType::StepN(n));
143 }
144 if let Some(_opcode) = s.strip_prefix("for ") {
145 todo!()
146 }
147 if let Some(cycle) = s.strip_prefix("at ") {
148 let cycle = cycle.trim().parse::<usize>().map_err(|err| {
149 format!("invalid breakpoint expression: could not parse cycle value: {err}")
150 })?;
151 return Ok(BreakpointType::StepTo(cycle));
152 }
153 if let Some(procedure) = s.strip_prefix("in ") {
154 let pattern = Pattern::new(procedure.trim())
155 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
156 return Ok(BreakpointType::Called(pattern));
157 }
158 match s.split_once(':') {
159 Some((file, line)) => {
160 let pattern = Pattern::new(file.trim())
161 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
162 let line = line.trim().parse::<u32>().map_err(|err| {
163 format!("invalid breakpoint expression: could not parse line: {err}")
164 })?;
165 Ok(BreakpointType::Line { pattern, line })
166 }
167 None => {
168 let pattern = Pattern::new(s.trim())
169 .map_err(|err| format!("invalid breakpoint expression: bad pattern: {err}"))?;
170 Ok(BreakpointType::File(pattern))
171 }
172 }
173 }
174}