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}