1use std::collections::HashMap;
2use std::sync::Arc;
3
4use either::Either;
5use hashes::{sha256, Hash, HashEngine};
6use simplicity::{hashes, Cmr};
7
8use crate::error::Span;
9use crate::types::ResolvedType;
10use crate::value::{StructuralValue, Value};
11
12#[derive(Debug, Clone, PartialEq, Eq, Default)]
16pub struct DebugSymbols(HashMap<Cmr, TrackedCall>);
17
18#[derive(Debug, Clone, Eq, PartialEq, Default)]
23pub(crate) struct CallTracker {
24 next_id: u32,
25 map: HashMap<Span, (Cmr, TrackedCallName)>,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
30pub struct TrackedCall {
31 text: Arc<str>,
32 name: TrackedCallName,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
37pub enum TrackedCallName {
38 Assert,
39 Panic,
40 Jet,
41 UnwrapLeft(ResolvedType),
42 UnwrapRight(ResolvedType),
43 Unwrap,
44 Debug(ResolvedType),
45}
46
47#[derive(Debug, Clone, Eq, PartialEq, Hash)]
49pub struct FallibleCall {
50 text: Arc<str>,
51 name: FallibleCallName,
52}
53
54#[derive(Debug, Clone, Eq, PartialEq, Hash)]
56pub enum FallibleCallName {
57 Assert,
58 Panic,
59 Jet,
60 UnwrapLeft(Value),
61 UnwrapRight(Value),
62 Unwrap,
63}
64
65#[derive(Debug, Clone, Eq, PartialEq, Hash)]
67pub struct DebugValue {
68 text: Arc<str>,
69 value: Value,
70}
71
72impl DebugSymbols {
73 pub(crate) fn insert(&mut self, span: Span, cmr: Cmr, name: TrackedCallName, file: &str) {
76 let text = remove_excess_whitespace(span.to_slice(file).unwrap_or(""));
77 let text = text
78 .strip_prefix("dbg!(")
79 .and_then(|s| s.strip_suffix(")"))
80 .unwrap_or(&text);
81
82 self.0.insert(
83 cmr,
84 TrackedCall {
85 text: Arc::from(text),
86 name,
87 },
88 );
89 }
90
91 pub fn contains_key(&self, cmr: &Cmr) -> bool {
93 self.0.contains_key(cmr)
94 }
95
96 pub fn get(&self, cmr: &Cmr) -> Option<&TrackedCall> {
98 self.0.get(cmr)
99 }
100}
101
102fn remove_excess_whitespace(s: &str) -> String {
103 let mut last_was_space = true;
104 let is_excess_whitespace = move |c: char| match c {
105 ' ' => std::mem::replace(&mut last_was_space, true),
106 '\n' => true,
107 _ => {
108 last_was_space = false;
109 false
110 }
111 };
112 s.replace(is_excess_whitespace, "")
113}
114
115impl CallTracker {
116 pub fn track_call(&mut self, span: Span, name: TrackedCallName) {
125 let cmr = self.next_id_cmr();
126 let _replaced = self.map.insert(span, (cmr, name));
127 self.next_id += 1;
128 }
129
130 pub fn get_cmr(&self, span: &Span) -> Option<Cmr> {
132 self.map.get(span).map(|x| x.0)
133 }
134
135 fn next_id_cmr(&self) -> Cmr {
136 let tag_hash = sha256::Hash::hash(b"simfony\x1fdebug\x1f");
137 let mut engine = sha256::Hash::engine();
138 engine.input(tag_hash.as_ref());
139 engine.input(tag_hash.as_ref());
140 engine.input(self.next_id.to_be_bytes().as_ref());
141 Cmr::from_byte_array(sha256::Hash::from_engine(engine).to_byte_array())
142 }
143
144 pub fn with_file(&self, file: &str) -> DebugSymbols {
146 let mut debug_symbols = DebugSymbols::default();
147 for (span, (cmr, name)) in &self.map {
148 debug_symbols.insert(*span, *cmr, name.clone(), file);
149 }
150 debug_symbols
151 }
152}
153
154impl TrackedCall {
155 pub fn text(&self) -> &str {
157 &self.text
158 }
159
160 pub fn name(&self) -> &TrackedCallName {
162 &self.name
163 }
164
165 pub fn map_value(&self, value: &StructuralValue) -> Option<Either<FallibleCall, DebugValue>> {
172 let name = match self.name() {
173 TrackedCallName::Assert => FallibleCallName::Assert,
174 TrackedCallName::Panic => FallibleCallName::Panic,
175 TrackedCallName::Jet => FallibleCallName::Jet,
176 TrackedCallName::UnwrapLeft(ty) => {
177 Value::reconstruct(value, ty).map(FallibleCallName::UnwrapLeft)?
178 }
179 TrackedCallName::UnwrapRight(ty) => {
180 Value::reconstruct(value, ty).map(FallibleCallName::UnwrapRight)?
181 }
182 TrackedCallName::Unwrap => FallibleCallName::Unwrap,
183 TrackedCallName::Debug(ty) => {
184 return Value::reconstruct(value, ty)
185 .map(|value| DebugValue {
186 text: Arc::clone(&self.text),
187 value,
188 })
189 .map(Either::Right)
190 }
191 };
192 Some(Either::Left(FallibleCall {
193 text: Arc::clone(&self.text),
194 name,
195 }))
196 }
197}
198
199impl FallibleCall {
200 pub fn text(&self) -> &str {
202 &self.text
203 }
204
205 pub fn name(&self) -> &FallibleCallName {
207 &self.name
208 }
209}
210
211impl DebugValue {
212 pub fn text(&self) -> &str {
214 &self.text
215 }
216
217 pub fn value(&self) -> &Value {
219 &self.value
220 }
221}