1use std::hash::{Hash, Hasher};
5
6use super::{
7 Timestamp,
8 constraint::Comparator,
9 value::{Error, NANOS, Val},
10};
11use crate::{
12 ast::Expr,
13 diag::Position,
14 paths::{UNKNOWN_ID, UNKNOWN_RUN},
15};
16
17#[derive(Debug, Clone)]
21pub enum Observed {
22 Null,
23 Bool(bool),
24 Int(i64),
25 Float(f64),
26 String(String),
27 Bytes(Vec<u8>),
28 Timestamp(Timestamp),
29 Duration(i64, i32),
31 LatLng(f64, f64),
33 Path(Vec<Segment>),
34 Map(Vec<(String, Observed)>),
36 List(Vec<Observed>),
37 Set(Vec<Observed>),
38 MapDiff(Vec<(String, KeyStatus)>),
40 Constraint(Vec<(Comparator, Observed)>),
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Hash)]
47pub enum Segment {
48 Simple(String),
49 Capture,
50 GlobCapture,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55pub enum KeyStatus {
56 Added,
58 Removed,
60 Changed,
61 Unchanged,
62}
63
64impl KeyStatus {
65 pub fn name(self) -> &'static str {
67 match self {
68 Self::Added => "ADDED",
69 Self::Removed => "REMOVED",
70 Self::Changed => "CHANGED",
71 Self::Unchanged => "UNCHANGED",
72 }
73 }
74}
75
76impl PartialEq for Observed {
79 fn eq(&self, other: &Self) -> bool {
80 match (self, other) {
81 (Self::Null, Self::Null) => true,
82 (Self::Bool(a), Self::Bool(b)) => a == b,
83 (Self::Int(a), Self::Int(b)) => a == b,
84 (Self::Float(a), Self::Float(b)) => bits(*a) == bits(*b),
85 (Self::String(a), Self::String(b)) => a == b,
86 (Self::Bytes(a), Self::Bytes(b)) => a == b,
87 (Self::Timestamp(a), Self::Timestamp(b)) => a == b,
88 (Self::Duration(a, b), Self::Duration(c, d)) => a == c && b == d,
89 (Self::LatLng(a, b), Self::LatLng(c, d)) => {
90 bits(*a) == bits(*c) && bits(*b) == bits(*d)
91 }
92 (Self::Path(a), Self::Path(b)) => a == b,
93 (Self::Map(a), Self::Map(b)) => a == b,
94 (Self::List(a), Self::List(b)) | (Self::Set(a), Self::Set(b)) => a == b,
95 (Self::MapDiff(a), Self::MapDiff(b)) => a == b,
96 (Self::Constraint(a), Self::Constraint(b)) => a == b,
97 _ => false,
98 }
99 }
100}
101
102impl Eq for Observed {}
103
104fn bits(f: f64) -> u64 {
105 if f.is_nan() {
106 f64::NAN.to_bits()
107 } else {
108 f.to_bits()
109 }
110}
111
112pub(crate) fn hash_val<H: Hasher>(v: &Val, state: &mut H) {
114 match v {
115 Val::Null | Val::Unknown => 0u8.hash(state),
116 Val::Bool(b) => (1u8, b).hash(state),
117 Val::Int(i) => (2u8, i).hash(state),
118 Val::Float(f) => (3u8, bits(*f)).hash(state),
119 Val::Str(s) => (4u8, &**s).hash(state),
120 Val::Bytes(b) => (5u8, &**b).hash(state),
121 Val::Timestamp(seconds, nanos) => (6u8, seconds, nanos).hash(state),
122 Val::Duration(nanos) => (7u8, nanos).hash(state),
123 Val::LatLng(lat, lng) => (8u8, bits(*lat), bits(*lng)).hash(state),
124 Val::Path(segments) => (9u8, &**segments).hash(state),
125 Val::Map(map) => {
126 (10u8, map.len()).hash(state);
127 for (k, v) in map.iter() {
128 k.hash(state);
129 hash_val(v, state);
130 }
131 }
132 Val::List(items) | Val::Set(items) => {
133 (
134 if matches!(v, Val::Set(_)) { 12u8 } else { 11u8 },
135 items.len(),
136 )
137 .hash(state);
138 for item in items.iter() {
139 hash_val(item, state);
140 }
141 }
142 Val::MapDiff(diff) => (13u8, &diff.statuses).hash(state),
143 Val::Constraint(simple) => {
144 (14u8, simple.len()).hash(state);
145 for s in simple.iter() {
146 s.comparator.hash(state);
147 hash_val(&s.value, state);
148 }
149 }
150 }
151}
152
153pub(crate) fn shows(o: &Observed, v: &Val) -> bool {
155 match (o, v) {
156 (Observed::Null, Val::Null | Val::Unknown) => true,
157 (Observed::Bool(a), Val::Bool(b)) => a == b,
158 (Observed::Int(a), Val::Int(b)) => a == b,
159 (Observed::Float(a), Val::Float(b)) => bits(*a) == bits(*b),
160 (Observed::String(a), Val::Str(b)) => a.as_str() == &**b,
161 (Observed::Bytes(a), Val::Bytes(b)) => a.as_slice() == &**b,
162 (Observed::Timestamp(t), Val::Timestamp(seconds, nanos)) => {
163 t.seconds == *seconds && i64::from(t.nanos) == i64::from(*nanos)
164 }
165 (Observed::Duration(..), Val::Duration(_)) | (Observed::Path(_), Val::Path(_)) => {
166 *o == observe(v)
167 }
168 (Observed::LatLng(a, b), Val::LatLng(c, d)) => bits(*a) == bits(*c) && bits(*b) == bits(*d),
169 (Observed::Map(a), Val::Map(b)) => {
170 a.len() == b.len()
171 && a.iter()
172 .zip(b.iter())
173 .all(|((k, x), (l, y))| k == l && shows(x, y))
174 }
175 (Observed::List(a), Val::List(b)) | (Observed::Set(a), Val::Set(b)) => {
176 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| shows(x, y))
177 }
178 (Observed::MapDiff(a), Val::MapDiff(b)) => *a == b.statuses,
179 (Observed::Constraint(a), Val::Constraint(b)) => {
180 a.len() == b.len()
181 && a.iter()
182 .zip(b.iter())
183 .all(|((c, x), s)| *c == s.comparator && shows(x, &s.value))
184 }
185 _ => false,
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq)]
191pub enum Outcome {
192 Value(Observed),
193 Error {
196 position: Option<Position>,
197 message: String,
198 },
199 Skipped,
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
206pub(crate) enum NodeKey {
207 Expr(usize, usize),
208 Body(usize),
209}
210
211impl NodeKey {
212 pub fn of(e: &Expr) -> Self {
213 Self::Expr(e.span.start, e.span.end)
214 }
215}
216
217#[derive(Clone)]
219pub(crate) enum Traced {
220 Value(Val),
221 Error(Error),
222 Skipped,
223}
224
225#[derive(Default)]
229pub struct Trace {
230 pub(crate) debug: Vec<Observed>,
231 pub(crate) values: Option<Vec<(NodeKey, Traced)>>,
232}
233
234impl Trace {
235 pub fn new() -> Self {
237 Self::default()
238 }
239
240 pub fn with_values() -> Self {
242 Self {
243 debug: Vec::new(),
244 values: Some(Vec::new()),
245 }
246 }
247
248 pub fn debug(&self) -> &[Observed] {
250 &self.debug
251 }
252
253 pub(crate) fn tracing(&self) -> bool {
254 self.values.is_some()
255 }
256
257 pub(crate) fn record(&mut self, key: NodeKey, result: &Result<Val, Error>) {
258 if let Some(values) = &mut self.values {
259 values.push((
260 key,
261 match result {
262 Ok(v) => Traced::Value(v.clone()),
263 Err(e) => Traced::Error(e.clone()),
264 },
265 ));
266 }
267 }
268
269 pub(crate) fn skip(&mut self, key: NodeKey) {
270 if let Some(values) = &mut self.values {
271 values.push((key, Traced::Skipped));
272 }
273 }
274
275 pub(crate) fn mark(&self) -> usize {
277 self.values.as_ref().map_or(0, Vec::len)
278 }
279
280 pub(crate) fn repeat(&mut self, marks: &[usize]) {
285 let Some(values) = &mut self.values else {
286 return;
287 };
288 let end = values.len();
289 for (i, &start) in marks.iter().enumerate() {
290 let stop = marks.get(i + 1).copied().unwrap_or(end);
291 for _ in i + 1..marks.len() {
292 for j in start..stop {
293 let again = values[j].clone();
294 values.push(again);
295 }
296 }
297 }
298 }
299}
300
301pub(crate) fn observe(v: &Val) -> Observed {
303 match v {
304 Val::Null | Val::Unknown => Observed::Null,
306 Val::Bool(b) => Observed::Bool(*b),
307 Val::Int(i) => Observed::Int(*i),
308 Val::Float(f) => Observed::Float(*f),
309 Val::Str(s) => Observed::String(s.to_string()),
310 Val::Bytes(b) => Observed::Bytes(b.to_vec()),
311 Val::List(items) => Observed::List(items.iter().map(observe).collect()),
312 Val::Set(items) => Observed::Set(items.iter().map(observe).collect()),
313 Val::Map(map) => Observed::Map(map.iter().map(|(k, v)| (k.clone(), observe(v))).collect()),
314 Val::MapDiff(diff) => Observed::MapDiff(diff.statuses.clone()),
315 Val::Timestamp(seconds, nanos) => Observed::Timestamp(Timestamp {
316 seconds: *seconds,
317 nanos: i32::try_from(*nanos).unwrap_or(0),
318 }),
319 Val::Duration(nanos) => Observed::Duration(
320 i64::try_from(nanos / NANOS).unwrap_or(0),
321 i32::try_from(nanos % NANOS).unwrap_or(0),
322 ),
323 Val::LatLng(lat, lng) => Observed::LatLng(*lat, *lng),
324 Val::Path(segments) => Observed::Path(
325 segments
326 .iter()
327 .map(|s| match s.as_str() {
328 UNKNOWN_ID => Segment::Capture,
329 UNKNOWN_RUN => Segment::GlobCapture,
330 s => Segment::Simple(s.to_owned()),
331 })
332 .collect(),
333 ),
334 Val::Constraint(simple) => Observed::Constraint(
335 simple
336 .iter()
337 .map(|s| (s.comparator, observe(&s.value)))
338 .collect(),
339 ),
340 }
341}