1use std::hash::{Hash, Hasher};
5
6use super::{
7 Method, 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 Undefined,
26 Bool(bool),
27 Int(i64),
28 Float(f64),
29 String(String),
30 Bytes(Vec<u8>),
31 Timestamp(Timestamp),
32 Duration(i64, i32),
34 LatLng(f64, f64),
36 Path(Vec<Segment>),
37 Map(Vec<(String, Observed)>),
39 List(Vec<Observed>),
40 Set(Vec<Observed>),
41 MapDiff(Vec<(String, KeyStatus)>),
43 Constraint(Vec<(Comparator, Observed)>),
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Hash)]
50pub enum Segment {
51 Simple(String),
52 Capture,
53 GlobCapture,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum KeyStatus {
59 Added,
61 Removed,
63 Changed,
64 Unchanged,
65}
66
67impl KeyStatus {
68 pub fn name(self) -> &'static str {
70 match self {
71 Self::Added => "ADDED",
72 Self::Removed => "REMOVED",
73 Self::Changed => "CHANGED",
74 Self::Unchanged => "UNCHANGED",
75 }
76 }
77}
78
79impl PartialEq for Observed {
82 fn eq(&self, other: &Self) -> bool {
83 match (self, other) {
84 (Self::Null, Self::Null) | (Self::Undefined, Self::Undefined) => true,
85 (Self::Bool(a), Self::Bool(b)) => a == b,
86 (Self::Int(a), Self::Int(b)) => a == b,
87 (Self::Float(a), Self::Float(b)) => bits(*a) == bits(*b),
88 (Self::String(a), Self::String(b)) => a == b,
89 (Self::Bytes(a), Self::Bytes(b)) => a == b,
90 (Self::Timestamp(a), Self::Timestamp(b)) => a == b,
91 (Self::Duration(a, b), Self::Duration(c, d)) => a == c && b == d,
92 (Self::LatLng(a, b), Self::LatLng(c, d)) => {
93 bits(*a) == bits(*c) && bits(*b) == bits(*d)
94 }
95 (Self::Path(a), Self::Path(b)) => a == b,
96 (Self::Map(a), Self::Map(b)) => a == b,
97 (Self::List(a), Self::List(b)) | (Self::Set(a), Self::Set(b)) => a == b,
98 (Self::MapDiff(a), Self::MapDiff(b)) => a == b,
99 (Self::Constraint(a), Self::Constraint(b)) => a == b,
100 _ => false,
101 }
102 }
103}
104
105impl Eq for Observed {}
106
107fn bits(f: f64) -> u64 {
108 if f.is_nan() {
109 f64::NAN.to_bits()
110 } else {
111 f.to_bits()
112 }
113}
114
115pub(crate) fn hash_val<H: Hasher>(v: &Val, state: &mut H) {
117 match v {
118 Val::Null => 0u8.hash(state),
119 Val::Unknown => 15u8.hash(state),
120 Val::Bool(b) => (1u8, b).hash(state),
121 Val::Int(i) => (2u8, i).hash(state),
122 Val::Float(f) => (3u8, bits(*f)).hash(state),
123 Val::Str(s) => (4u8, &**s).hash(state),
124 Val::Bytes(b) => (5u8, &**b).hash(state),
125 Val::Timestamp(seconds, nanos) => (6u8, seconds, nanos).hash(state),
126 Val::Duration(nanos) => (7u8, nanos).hash(state),
127 Val::LatLng(lat, lng) => (8u8, bits(*lat), bits(*lng)).hash(state),
128 Val::Path(segments) => (9u8, &**segments).hash(state),
129 Val::Map(map) => {
130 (10u8, map.len()).hash(state);
131 for (k, v) in map.iter() {
132 k.hash(state);
133 hash_val(v, state);
134 }
135 }
136 Val::List(items) | Val::Set(items) => {
137 (
138 if matches!(v, Val::Set(_)) { 12u8 } else { 11u8 },
139 items.len(),
140 )
141 .hash(state);
142 for item in items.iter() {
143 hash_val(item, state);
144 }
145 }
146 Val::MapDiff(diff) => (13u8, &diff.statuses).hash(state),
147 Val::Constraint(simple) => {
148 (14u8, simple.len()).hash(state);
149 for s in simple.iter() {
150 s.comparator.hash(state);
151 hash_val(&s.value, state);
152 }
153 }
154 }
155}
156
157pub(crate) fn shows(o: &Observed, v: &Val) -> bool {
159 match (o, v) {
160 (Observed::Null, Val::Null) | (Observed::Undefined, Val::Unknown) => true,
161 (Observed::Bool(a), Val::Bool(b)) => a == b,
162 (Observed::Int(a), Val::Int(b)) => a == b,
163 (Observed::Float(a), Val::Float(b)) => bits(*a) == bits(*b),
164 (Observed::String(a), Val::Str(b)) => a.as_str() == &**b,
165 (Observed::Bytes(a), Val::Bytes(b)) => a.as_slice() == &**b,
166 (Observed::Timestamp(t), Val::Timestamp(seconds, nanos)) => {
167 t.seconds == *seconds && i64::from(t.nanos) == i64::from(*nanos)
168 }
169 (Observed::Duration(..), Val::Duration(_)) | (Observed::Path(_), Val::Path(_)) => {
170 *o == observe(v)
171 }
172 (Observed::LatLng(a, b), Val::LatLng(c, d)) => bits(*a) == bits(*c) && bits(*b) == bits(*d),
173 (Observed::Map(a), Val::Map(b)) => {
174 a.len() == b.len()
175 && a.iter()
176 .zip(b.iter())
177 .all(|((k, x), (l, y))| k == l && shows(x, y))
178 }
179 (Observed::List(a), Val::List(b)) | (Observed::Set(a), Val::Set(b)) => {
180 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| shows(x, y))
181 }
182 (Observed::MapDiff(a), Val::MapDiff(b)) => *a == b.statuses,
183 (Observed::Constraint(a), Val::Constraint(b)) => {
184 a.len() == b.len()
185 && a.iter()
186 .zip(b.iter())
187 .all(|((c, x), s)| *c == s.comparator && shows(x, &s.value))
188 }
189 _ => false,
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
195pub enum Outcome {
196 Value(Observed),
197 Error {
200 position: Option<Position>,
201 message: String,
202 },
203 Skipped,
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
210pub(crate) enum NodeKey {
211 Expr(usize, usize),
212 Body(usize),
213}
214
215impl NodeKey {
216 pub fn of(e: &Expr) -> Self {
217 Self::Expr(e.span.start, e.span.end)
218 }
219}
220
221#[derive(Clone)]
223pub(crate) enum Traced {
224 Value(Val),
225 Error(Error),
226 Skipped,
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231pub enum Verdict {
232 Allow,
233 False,
234 Error,
235}
236
237#[derive(Debug, Clone)]
243pub struct Evaluation {
244 pub method: Method,
245 pub request: Observed,
246 pub resource: Observed,
247 pub statements: Vec<(u32, Verdict)>,
248}
249
250#[derive(Default)]
255pub struct Trace {
256 pub(crate) debug: Vec<Observed>,
257 pub(crate) values: Option<Vec<(NodeKey, Traced)>>,
258 pub(crate) evaluations: Option<Vec<Evaluation>>,
259}
260
261impl Trace {
262 pub fn new() -> Self {
264 Self::default()
265 }
266
267 pub fn with_values() -> Self {
269 Self {
270 values: Some(Vec::new()),
271 ..Self::default()
272 }
273 }
274
275 #[must_use]
277 pub fn with_evaluations(mut self) -> Self {
278 self.evaluations = Some(Vec::new());
279 self
280 }
281
282 pub fn debug(&self) -> &[Observed] {
284 &self.debug
285 }
286
287 pub fn evaluations(&self) -> &[Evaluation] {
289 self.evaluations.as_deref().unwrap_or_default()
290 }
291
292 pub(crate) fn evaluating(
295 &mut self,
296 method: Method,
297 request: &Val,
298 resource: &Val,
299 ) -> Option<usize> {
300 let evaluations = self.evaluations.as_mut()?;
301 evaluations.push(Evaluation {
302 method,
303 request: observe(request),
304 resource: observe(resource),
305 statements: Vec::new(),
306 });
307 Some(evaluations.len() - 1)
308 }
309
310 pub(crate) fn verdict(&mut self, at: Option<usize>, line: u32, verdict: Verdict) {
312 if let (Some(at), Some(evaluations)) = (at, &mut self.evaluations) {
313 evaluations[at].statements.push((line, verdict));
314 }
315 }
316
317 pub(crate) fn tracing(&self) -> bool {
318 self.values.is_some()
319 }
320
321 pub(crate) fn record(&mut self, key: NodeKey, result: &Result<Val, Error>) {
322 if let Some(values) = &mut self.values {
323 values.push((
324 key,
325 match result {
326 Ok(v) => Traced::Value(v.clone()),
327 Err(e) => Traced::Error(e.clone()),
328 },
329 ));
330 }
331 }
332
333 pub(crate) fn skip(&mut self, key: NodeKey) {
334 if let Some(values) = &mut self.values {
335 values.push((key, Traced::Skipped));
336 }
337 }
338
339 pub(crate) fn mark(&self) -> usize {
341 self.values.as_ref().map_or(0, Vec::len)
342 }
343
344 pub(crate) fn repeat(&mut self, marks: &[usize]) {
349 let Some(values) = &mut self.values else {
350 return;
351 };
352 let end = values.len();
353 for (i, &start) in marks.iter().enumerate() {
354 let stop = marks.get(i + 1).copied().unwrap_or(end);
355 for _ in i + 1..marks.len() {
356 for j in start..stop {
357 let again = values[j].clone();
358 values.push(again);
359 }
360 }
361 }
362 }
363}
364
365pub(crate) fn observe(v: &Val) -> Observed {
367 match v {
368 Val::Null => Observed::Null,
369 Val::Unknown => Observed::Undefined,
371 Val::Bool(b) => Observed::Bool(*b),
372 Val::Int(i) => Observed::Int(*i),
373 Val::Float(f) => Observed::Float(*f),
374 Val::Str(s) => Observed::String(s.to_string()),
375 Val::Bytes(b) => Observed::Bytes(b.to_vec()),
376 Val::List(items) => Observed::List(items.iter().map(observe).collect()),
377 Val::Set(items) => Observed::Set(items.iter().map(observe).collect()),
378 Val::Map(map) => Observed::Map(map.iter().map(|(k, v)| (k.clone(), observe(v))).collect()),
379 Val::MapDiff(diff) => Observed::MapDiff(diff.statuses.clone()),
380 Val::Timestamp(seconds, nanos) => Observed::Timestamp(Timestamp {
381 seconds: *seconds,
382 nanos: i32::try_from(*nanos).unwrap_or(0),
383 }),
384 Val::Duration(nanos) => Observed::Duration(
385 i64::try_from(nanos / NANOS).unwrap_or(0),
386 i32::try_from(nanos % NANOS).unwrap_or(0),
387 ),
388 Val::LatLng(lat, lng) => Observed::LatLng(*lat, *lng),
389 Val::Path(segments) => Observed::Path(
390 segments
391 .iter()
392 .map(|s| match s.as_str() {
393 UNKNOWN_ID => Segment::Capture,
394 UNKNOWN_RUN => Segment::GlobCapture,
395 s => Segment::Simple(s.to_owned()),
396 })
397 .collect(),
398 ),
399 Val::Constraint(simple) => Observed::Constraint(
400 simple
401 .iter()
402 .map(|s| (s.comparator, observe(&s.value)))
403 .collect(),
404 ),
405 }
406}