Skip to main content

graphrecords_query/operations/errors/
inspection.rs

1use crate::{
2    Bare, BareValueDomain, Diagnostic, ErrorGroup, Explain, Failure, FailureKind, FailureKindValue,
3    FailureValue, IndexDomain, Indexed, Mask, Operand, QueryResult, Scalar, ValueDomain,
4    element::{Dropping, Pipeline, Preserving},
5    execution::EvaluationCache,
6    explain::ExplainFormatter,
7    operations::{Apply, ElementKernel, ElementPipeline, Operation, OperationContext, Prepare},
8    optimizer::{OperationInputs, OptimizerHints, PlanIdentity, PlanInputs},
9    registry::operation_manifest,
10    traits::{ErrorKind, ErrorKindName, Errors, HasErrorCause, InErrorGroup, IsErrorKind},
11};
12use graphrecords_core::{GraphRecord, graphrecord::GraphRecordValue};
13use std::{
14    any::type_name,
15    error::Error,
16    fmt::{self, Write},
17    marker::PhantomData,
18};
19
20#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
21#[operation(scope = Element)]
22#[explain(label = "Errors")]
23#[plan(optimizer_hints(empty = if_any))]
24pub struct ErrorsOperation;
25
26impl Prepare for ErrorsOperation {
27    type Prepared<'a> = ();
28
29    fn prepare<'a>(
30        &'a self,
31        _graphrecord: &'a GraphRecord,
32        _cache: &'a EvaluationCache<'a>,
33    ) -> QueryResult<Self::Prepared<'a>> {
34        Ok(())
35    }
36}
37
38impl<I: IndexDomain, V: ValueDomain> ElementKernel<Indexed<I, V>> for ErrorsOperation {
39    type Emission = Dropping;
40    type OutShape = Indexed<I, FailureValue>;
41
42    fn pipeline<'a>(
43        _graphrecord: &'a GraphRecord,
44        _prepared: Self::Prepared<'a>,
45    ) -> QueryResult<ElementPipeline<'a, Indexed<I, V>, Self>> {
46        Ok(Pipeline::unkeyed(|result: QueryResult<_>| {
47            result.err().map(|failure| Ok(*failure))
48        }))
49    }
50}
51
52impl<V: BareValueDomain> ElementKernel<Bare<V>> for ErrorsOperation {
53    type Emission = Dropping;
54    type OutShape = Bare<FailureValue>;
55
56    fn pipeline<'a>(
57        _graphrecord: &'a GraphRecord,
58        _prepared: Self::Prepared<'a>,
59    ) -> QueryResult<ElementPipeline<'a, Bare<V>, Self>> {
60        Ok(Pipeline::new(|result: QueryResult<_>| {
61            result.err().map(|failure| Ok(*failure))
62        }))
63    }
64}
65
66impl<O: Apply<ErrorsOperation>> Errors for O {
67    type ReturnOperand = O::Output;
68
69    fn errors(&self) -> Self::ReturnOperand {
70        Self::ReturnOperand::new(OperationContext::new(self.clone(), ErrorsOperation))
71    }
72}
73
74pub(super) mod errors {
75    use super::{
76        Bare, Dropping, Errors, ErrorsOperation, FailureValue, Indexed, operation_manifest,
77    };
78
79    operation_manifest! {
80        ErrorsOperation {
81            method: Errors::errors;
82            scope: element;
83
84            kernel {
85                parameters: <I: IndexDomain, V: ValueDomain>;
86                input: Indexed<I, V>;
87                output: Indexed<I, FailureValue>;
88                emission: Dropping;
89            }
90            kernel {
91                parameters: <V: BareValueDomain>;
92                input: Bare<V>;
93                output: Bare<FailureValue>;
94                emission: Dropping;
95            }
96        }
97    }
98}
99
100#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
101#[operation(scope = Element)]
102#[explain(label = "ErrorKind")]
103#[plan(optimizer_hints(
104    commutes_with_filter,
105    allows_limit_pushdown,
106    empty = if_any
107))]
108pub struct ErrorKindOperation;
109
110impl Prepare for ErrorKindOperation {
111    type Prepared<'a> = ();
112
113    fn prepare<'a>(
114        &'a self,
115        _graphrecord: &'a GraphRecord,
116        _cache: &'a EvaluationCache<'a>,
117    ) -> QueryResult<Self::Prepared<'a>> {
118        Ok(())
119    }
120}
121
122impl<I: IndexDomain> ElementKernel<Indexed<I, FailureValue>> for ErrorKindOperation {
123    type Emission = Preserving;
124    type OutShape = Indexed<I, FailureKindValue>;
125
126    fn pipeline<'a>(
127        _graphrecord: &'a GraphRecord,
128        _prepared: Self::Prepared<'a>,
129    ) -> QueryResult<ElementPipeline<'a, Indexed<I, FailureValue>, Self>> {
130        Ok(Pipeline::unkeyed(|result: QueryResult<Failure>| {
131            result.map(|failure| failure.kind())
132        }))
133    }
134}
135
136impl ElementKernel<Bare<FailureValue>> for ErrorKindOperation {
137    type Emission = Preserving;
138    type OutShape = Bare<FailureKindValue>;
139
140    fn pipeline<'a>(
141        _graphrecord: &'a GraphRecord,
142        _prepared: Self::Prepared<'a>,
143    ) -> QueryResult<ElementPipeline<'a, Bare<FailureValue>, Self>> {
144        Ok(Pipeline::new(|result: QueryResult<Failure>| {
145            result.map(|failure| failure.kind())
146        }))
147    }
148}
149
150impl<O: Apply<ErrorKindOperation>> ErrorKind for O {
151    type ReturnOperand = O::Output;
152
153    fn kind(&self) -> Self::ReturnOperand {
154        Self::ReturnOperand::new(OperationContext::new(self.clone(), ErrorKindOperation))
155    }
156}
157
158pub(super) mod kind {
159    use super::{
160        Bare, ErrorKind, ErrorKindOperation, FailureKindValue, FailureValue, Indexed, Preserving,
161        operation_manifest,
162    };
163
164    operation_manifest! {
165        ErrorKindOperation {
166            method: ErrorKind::kind;
167            scope: element;
168
169            kernel {
170                parameters: <I: IndexDomain>;
171                input: Indexed<I, FailureValue>;
172                output: Indexed<I, FailureKindValue>;
173                emission: Preserving;
174            }
175            kernel {
176                parameters: <>;
177                input: Bare<FailureValue>;
178                output: Bare<FailureKindValue>;
179                emission: Preserving;
180            }
181        }
182    }
183}
184
185#[derive(Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
186#[operation(scope = Element)]
187#[plan(optimizer_hints(
188    commutes_with_filter,
189    allows_limit_pushdown,
190    empty = if_any
191))]
192pub struct IsErrorKindOperation<D: Diagnostic> {
193    marker: PhantomData<fn() -> D>,
194}
195
196impl<D: Diagnostic> IsErrorKindOperation<D> {
197    const fn new() -> Self {
198        Self {
199            marker: PhantomData,
200        }
201    }
202}
203
204impl<D: Diagnostic> Clone for IsErrorKindOperation<D> {
205    fn clone(&self) -> Self {
206        Self::new()
207    }
208}
209
210impl<D: Diagnostic> Explain for IsErrorKindOperation<D> {
211    fn describe<'a>(&'a self, formatter: &mut ExplainFormatter<'a, '_>) -> fmt::Result {
212        write!(formatter, "IsErrorKind kind={}", D::name())
213    }
214}
215
216impl<D: Diagnostic> Prepare for IsErrorKindOperation<D> {
217    type Prepared<'a> = ();
218
219    fn prepare<'a>(
220        &'a self,
221        _graphrecord: &'a GraphRecord,
222        _cache: &'a EvaluationCache<'a>,
223    ) -> QueryResult<Self::Prepared<'a>> {
224        Ok(())
225    }
226}
227
228impl<I: IndexDomain, D: Diagnostic> ElementKernel<Indexed<I, FailureValue>>
229    for IsErrorKindOperation<D>
230{
231    type Emission = Preserving;
232    type OutShape = Indexed<I, Mask>;
233
234    fn pipeline<'a>(
235        _graphrecord: &'a GraphRecord,
236        _prepared: Self::Prepared<'a>,
237    ) -> QueryResult<ElementPipeline<'a, Indexed<I, FailureValue>, Self>> {
238        Ok(Pipeline::unkeyed(|result: QueryResult<Failure>| {
239            result.map(|failure| failure.is_kind::<D>())
240        }))
241    }
242}
243
244impl<D: Diagnostic> ElementKernel<Bare<FailureValue>> for IsErrorKindOperation<D> {
245    type Emission = Preserving;
246    type OutShape = Bare<Mask>;
247
248    fn pipeline<'a>(
249        _graphrecord: &'a GraphRecord,
250        _prepared: Self::Prepared<'a>,
251    ) -> QueryResult<ElementPipeline<'a, Bare<FailureValue>, Self>> {
252        Ok(Pipeline::new(|result: QueryResult<Failure>| {
253            result.map(|failure| failure.is_kind::<D>())
254        }))
255    }
256}
257
258impl<O: Operand> IsErrorKind for O {
259    type ReturnOperand<D>
260        = O::Output
261    where
262        D: Diagnostic,
263        O: Apply<IsErrorKindOperation<D>>;
264
265    fn is<D>(&self) -> Self::ReturnOperand<D>
266    where
267        D: Diagnostic,
268        Self: Apply<IsErrorKindOperation<D>>,
269    {
270        Self::ReturnOperand::new(OperationContext::new(
271            self.clone(),
272            IsErrorKindOperation::new(),
273        ))
274    }
275}
276
277#[derive(Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
278#[operation(scope = Element)]
279#[plan(optimizer_hints(
280    commutes_with_filter,
281    allows_limit_pushdown,
282    empty = if_any
283))]
284pub struct InErrorGroupOperation<G: ErrorGroup> {
285    marker: PhantomData<fn() -> G>,
286}
287
288impl<G: ErrorGroup> InErrorGroupOperation<G> {
289    const fn new() -> Self {
290        Self {
291            marker: PhantomData,
292        }
293    }
294}
295
296impl<G: ErrorGroup> Clone for InErrorGroupOperation<G> {
297    fn clone(&self) -> Self {
298        Self::new()
299    }
300}
301
302impl<G: ErrorGroup> Explain for InErrorGroupOperation<G> {
303    fn describe<'a>(&'a self, formatter: &mut ExplainFormatter<'a, '_>) -> fmt::Result {
304        write!(formatter, "InErrorGroup group={}", G::name())
305    }
306}
307
308impl<G: ErrorGroup> Prepare for InErrorGroupOperation<G> {
309    type Prepared<'a> = ();
310
311    fn prepare<'a>(
312        &'a self,
313        _graphrecord: &'a GraphRecord,
314        _cache: &'a EvaluationCache<'a>,
315    ) -> QueryResult<Self::Prepared<'a>> {
316        Ok(())
317    }
318}
319
320impl<I: IndexDomain, G: ErrorGroup> ElementKernel<Indexed<I, FailureValue>>
321    for InErrorGroupOperation<G>
322{
323    type Emission = Preserving;
324    type OutShape = Indexed<I, Mask>;
325
326    fn pipeline<'a>(
327        _graphrecord: &'a GraphRecord,
328        _prepared: Self::Prepared<'a>,
329    ) -> QueryResult<ElementPipeline<'a, Indexed<I, FailureValue>, Self>> {
330        Ok(Pipeline::unkeyed(|result: QueryResult<Failure>| {
331            result.map(|failure| G::contains(&failure.kind()))
332        }))
333    }
334}
335
336impl<G: ErrorGroup> ElementKernel<Bare<FailureValue>> for InErrorGroupOperation<G> {
337    type Emission = Preserving;
338    type OutShape = Bare<Mask>;
339
340    fn pipeline<'a>(
341        _graphrecord: &'a GraphRecord,
342        _prepared: Self::Prepared<'a>,
343    ) -> QueryResult<ElementPipeline<'a, Bare<FailureValue>, Self>> {
344        Ok(Pipeline::new(|result: QueryResult<Failure>| {
345            result.map(|failure| G::contains(&failure.kind()))
346        }))
347    }
348}
349
350impl<O: Operand> InErrorGroup for O {
351    type ReturnOperand<G>
352        = O::Output
353    where
354        G: ErrorGroup,
355        O: Apply<InErrorGroupOperation<G>>;
356
357    fn in_error_group<G>(&self) -> Self::ReturnOperand<G>
358    where
359        G: ErrorGroup,
360        Self: Apply<InErrorGroupOperation<G>>,
361    {
362        Self::ReturnOperand::new(OperationContext::new(
363            self.clone(),
364            InErrorGroupOperation::new(),
365        ))
366    }
367}
368
369#[derive(Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
370#[operation(scope = Element)]
371#[plan(optimizer_hints(
372    commutes_with_filter,
373    allows_limit_pushdown,
374    empty = if_any
375))]
376pub struct HasErrorCauseOperation<C: Error + 'static> {
377    marker: PhantomData<fn() -> C>,
378}
379
380impl<C: Error + 'static> HasErrorCauseOperation<C> {
381    const fn new() -> Self {
382        Self {
383            marker: PhantomData,
384        }
385    }
386}
387
388impl<C: Error + 'static> Clone for HasErrorCauseOperation<C> {
389    fn clone(&self) -> Self {
390        Self::new()
391    }
392}
393
394impl<C: Error + 'static> Explain for HasErrorCauseOperation<C> {
395    fn describe<'a>(&'a self, formatter: &mut ExplainFormatter<'a, '_>) -> fmt::Result {
396        write!(formatter, "HasErrorCause cause={}", type_name::<C>())
397    }
398}
399
400impl<C: Error + 'static> Prepare for HasErrorCauseOperation<C> {
401    type Prepared<'a> = ();
402
403    fn prepare<'a>(
404        &'a self,
405        _graphrecord: &'a GraphRecord,
406        _cache: &'a EvaluationCache<'a>,
407    ) -> QueryResult<Self::Prepared<'a>> {
408        Ok(())
409    }
410}
411
412impl<I: IndexDomain, C: Error + 'static> ElementKernel<Indexed<I, FailureValue>>
413    for HasErrorCauseOperation<C>
414{
415    type Emission = Preserving;
416    type OutShape = Indexed<I, Mask>;
417
418    fn pipeline<'a>(
419        _graphrecord: &'a GraphRecord,
420        _prepared: Self::Prepared<'a>,
421    ) -> QueryResult<ElementPipeline<'a, Indexed<I, FailureValue>, Self>> {
422        Ok(Pipeline::unkeyed(|result: QueryResult<Failure>| {
423            result.map(|failure| failure.has_cause::<C>())
424        }))
425    }
426}
427
428impl<C: Error + 'static> ElementKernel<Bare<FailureValue>> for HasErrorCauseOperation<C> {
429    type Emission = Preserving;
430    type OutShape = Bare<Mask>;
431
432    fn pipeline<'a>(
433        _graphrecord: &'a GraphRecord,
434        _prepared: Self::Prepared<'a>,
435    ) -> QueryResult<ElementPipeline<'a, Bare<FailureValue>, Self>> {
436        Ok(Pipeline::new(|result: QueryResult<Failure>| {
437            result.map(|failure| failure.has_cause::<C>())
438        }))
439    }
440}
441
442impl<O: Operand> HasErrorCause for O {
443    type ReturnOperand<C>
444        = O::Output
445    where
446        C: Error + 'static,
447        O: Apply<HasErrorCauseOperation<C>>;
448
449    fn has_cause<C>(&self) -> Self::ReturnOperand<C>
450    where
451        C: Error + 'static,
452        Self: Apply<HasErrorCauseOperation<C>>,
453    {
454        Self::ReturnOperand::new(OperationContext::new(
455            self.clone(),
456            HasErrorCauseOperation::new(),
457        ))
458    }
459}
460
461#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
462#[operation(scope = Element)]
463#[explain(label = "ErrorKindName")]
464#[plan(optimizer_hints(
465    commutes_with_filter,
466    allows_limit_pushdown,
467    empty = if_any
468))]
469pub struct ErrorKindNameOperation;
470
471impl Prepare for ErrorKindNameOperation {
472    type Prepared<'a> = ();
473
474    fn prepare<'a>(
475        &'a self,
476        _graphrecord: &'a GraphRecord,
477        _cache: &'a EvaluationCache<'a>,
478    ) -> QueryResult<Self::Prepared<'a>> {
479        Ok(())
480    }
481}
482
483impl<I: IndexDomain> ElementKernel<Indexed<I, FailureKindValue>> for ErrorKindNameOperation {
484    type Emission = Preserving;
485    type OutShape = Indexed<I, Scalar>;
486
487    fn pipeline<'a>(
488        _graphrecord: &'a GraphRecord,
489        _prepared: Self::Prepared<'a>,
490    ) -> QueryResult<ElementPipeline<'a, Indexed<I, FailureKindValue>, Self>> {
491        Ok(Pipeline::unkeyed(|result: QueryResult<FailureKind>| {
492            result.map(|kind| GraphRecordValue::from(kind.name()))
493        }))
494    }
495}
496
497impl ElementKernel<Bare<FailureKindValue>> for ErrorKindNameOperation {
498    type Emission = Preserving;
499    type OutShape = Bare<Scalar>;
500
501    fn pipeline<'a>(
502        _graphrecord: &'a GraphRecord,
503        _prepared: Self::Prepared<'a>,
504    ) -> QueryResult<ElementPipeline<'a, Bare<FailureKindValue>, Self>> {
505        Ok(Pipeline::new(|result: QueryResult<FailureKind>| {
506            result.map(|kind| GraphRecordValue::from(kind.name()))
507        }))
508    }
509}
510
511impl<O: Apply<ErrorKindNameOperation>> ErrorKindName for O {
512    type ReturnOperand = O::Output;
513
514    fn name(&self) -> Self::ReturnOperand {
515        Self::ReturnOperand::new(OperationContext::new(self.clone(), ErrorKindNameOperation))
516    }
517}
518
519pub(super) mod name {
520    use super::{
521        Bare, ErrorKindName, ErrorKindNameOperation, FailureKindValue, Indexed, Preserving, Scalar,
522        operation_manifest,
523    };
524
525    operation_manifest! {
526        ErrorKindNameOperation {
527            method: ErrorKindName::name;
528            scope: element;
529
530            kernel {
531                parameters: <I: IndexDomain>;
532                input: Indexed<I, FailureKindValue>;
533                output: Indexed<I, Scalar>;
534                emission: Preserving;
535            }
536            kernel {
537                parameters: <>;
538                input: Bare<FailureKindValue>;
539                output: Bare<Scalar>;
540                emission: Preserving;
541            }
542        }
543    }
544}