1use std::{
12 fmt::{Display, Formatter},
13 mem,
14 ops::{Deref, DerefMut},
15};
16
17use serde::{Deserialize, Serialize, de, ser};
18
19mod diagnostic;
20pub mod r#macro;
21pub mod render;
22pub mod util;
23
24use std::{array::TryFromSliceError, error, fmt, num, string};
25
26use render::DefaultRenderer;
27
28use crate::{fragment::Fragment, value::r#type::Type};
29
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31pub struct OperatorChainEntry {
32 pub node_id: u64,
33 pub operator_name: String,
34 pub operator_version: String,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
38pub struct Diagnostic {
39 pub code: String,
40 pub rql: Option<String>,
41 pub message: String,
42 pub column: Option<DiagnosticColumn>,
43 pub fragment: Fragment,
44 pub label: Option<String>,
45 pub help: Option<String>,
46 pub notes: Vec<String>,
47 pub cause: Option<Box<Diagnostic>>,
48
49 pub operator_chain: Option<Vec<OperatorChainEntry>>,
50}
51
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub struct DiagnosticColumn {
54 pub name: String,
55 pub r#type: Type,
56}
57
58impl Default for Diagnostic {
59 fn default() -> Self {
60 Self {
61 code: String::new(),
62 rql: None,
63 message: String::new(),
64 column: None,
65 fragment: Fragment::None,
66 label: None,
67 help: None,
68 notes: Vec::new(),
69 cause: None,
70 operator_chain: None,
71 }
72 }
73}
74
75impl Display for Diagnostic {
76 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
77 f.write_fmt(format_args!("{}", self.code))
78 }
79}
80
81impl Diagnostic {
82 pub fn with_rql(&mut self, rql: String) {
83 self.rql = Some(rql.clone());
84
85 if let Some(ref mut cause) = self.cause {
86 let mut updated_cause = mem::take(cause.as_mut());
87 updated_cause.with_rql(rql);
88 **cause = updated_cause;
89 }
90 }
91
92 pub fn with_fragment(&mut self, new_fragment: Fragment) {
93 self.fragment = new_fragment;
94
95 if let Some(ref mut cause) = self.cause {
96 cause.with_fragment(self.fragment.clone());
97 }
98 }
99
100 pub fn fragment(&self) -> Option<Fragment> {
101 match &self.fragment {
102 Fragment::Statement {
103 ..
104 } => Some(self.fragment.clone()),
105 _ => None,
106 }
107 }
108}
109
110pub trait IntoDiagnostic {
111 fn into_diagnostic(self) -> Diagnostic;
112}
113
114#[derive(Debug, Clone, PartialEq)]
115pub enum UnaryOp {
116 Not,
117}
118
119impl Display for UnaryOp {
120 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
121 match self {
122 UnaryOp::Not => f.write_str("NOT"),
123 }
124 }
125}
126
127#[derive(Debug, Clone, PartialEq)]
128pub enum BinaryOp {
129 Add,
130 Sub,
131 Mul,
132 Div,
133 Rem,
134 Equal,
135 NotEqual,
136 LessThan,
137 LessThanEqual,
138 GreaterThan,
139 GreaterThanEqual,
140 Between,
141}
142
143impl BinaryOp {
144 pub fn symbol(&self) -> &'static str {
145 match self {
146 BinaryOp::Add => "+",
147 BinaryOp::Sub => "-",
148 BinaryOp::Mul => "*",
149 BinaryOp::Div => "/",
150 BinaryOp::Rem => "%",
151 BinaryOp::Equal => "==",
152 BinaryOp::NotEqual => "!=",
153 BinaryOp::LessThan => "<",
154 BinaryOp::LessThanEqual => "<=",
155 BinaryOp::GreaterThan => ">",
156 BinaryOp::GreaterThanEqual => ">=",
157 BinaryOp::Between => "BETWEEN",
158 }
159 }
160}
161
162impl Display for BinaryOp {
163 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
164 f.write_str(self.symbol())
165 }
166}
167
168#[derive(Debug, Clone, PartialEq)]
169pub enum LogicalOp {
170 Not,
171 And,
172 Or,
173 Xor,
174}
175
176impl Display for LogicalOp {
177 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
178 match self {
179 LogicalOp::Not => f.write_str("NOT"),
180 LogicalOp::And => f.write_str("AND"),
181 LogicalOp::Or => f.write_str("OR"),
182 LogicalOp::Xor => f.write_str("XOR"),
183 }
184 }
185}
186
187#[derive(Debug, Clone, PartialEq)]
188pub enum OperandCategory {
189 Number,
190 Text,
191 Temporal,
192 Uuid,
193}
194
195impl Display for OperandCategory {
196 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
197 match self {
198 OperandCategory::Number => f.write_str("number"),
199 OperandCategory::Text => f.write_str("text"),
200 OperandCategory::Temporal => f.write_str("temporal value"),
201 OperandCategory::Uuid => f.write_str("UUID"),
202 }
203 }
204}
205
206#[derive(Debug, Clone, PartialEq)]
207pub enum ConstraintKind {
208 Utf8MaxBytes {
209 actual: usize,
210 max: usize,
211 },
212 BlobMaxBytes {
213 actual: usize,
214 max: usize,
215 },
216 IntMaxBytes {
217 actual: usize,
218 max: usize,
219 },
220 UintMaxBytes {
221 actual: usize,
222 max: usize,
223 },
224 DecimalPrecision {
225 actual: u8,
226 max: u8,
227 },
228 DecimalScale {
229 actual: u8,
230 max: u8,
231 },
232 NoneNotAllowed {
233 column_type: Type,
234 },
235}
236
237#[derive(Debug, Clone, PartialEq)]
238pub enum TemporalKind {
239 InvalidDateFormat,
240 InvalidDateTimeFormat,
241 InvalidTimeFormat,
242 InvalidDurationFormat,
243 InvalidYear,
244 InvalidTimeComponentFormat {
245 component: String,
246 },
247 InvalidMonth,
248 InvalidDay,
249 InvalidHour,
250 InvalidMinute,
251 InvalidSecond,
252 InvalidFractionalSeconds,
253 InvalidDateValues,
254 InvalidTimeValues,
255 InvalidDurationCharacter,
256 IncompleteDurationSpecification,
257 InvalidUnitInContext {
258 unit: char,
259 in_time_part: bool,
260 },
261 InvalidDurationComponentValue {
262 unit: char,
263 },
264 UnrecognizedTemporalPattern,
265 EmptyDateComponent,
266 EmptyTimeComponent,
267 DuplicateDurationComponent {
268 component: char,
269 },
270 OutOfOrderDurationComponent {
271 component: char,
272 },
273 DateTimeOutOfRange,
274 DateTimeOverflow {
275 message: String,
276 },
277 DurationOverflow {
278 message: String,
279 },
280 DurationMixedSign {
281 days: i32,
282 nanos: i64,
283 },
284 TimeOverflow {
285 message: String,
286 },
287 DateOverflow {
288 message: String,
289 },
290}
291
292#[derive(Debug, Clone, PartialEq)]
293pub enum BlobEncodingKind {
294 InvalidHex,
295 InvalidBase64,
296 InvalidBase64Url,
297 InvalidBase58,
298 InvalidUtf8Sequence {
299 error: String,
300 },
301}
302
303#[derive(Debug, Clone, PartialEq)]
304pub enum AstErrorKind {
305 TokenizeError {
306 message: String,
307 },
308 UnexpectedEof,
309 ExpectedIdentifier,
310 InvalidColumnProperty,
311 InvalidPolicy,
312 UnexpectedToken {
313 expected: String,
314 },
315 UnsupportedToken,
316 MultipleExpressionsWithoutBraces,
317 UnrecognizedType,
318 UnsupportedAstNode {
319 node_type: String,
320 },
321 EmptyPipeline,
322}
323
324#[derive(Debug, Clone, PartialEq)]
325pub enum ProcedureErrorKind {
326 UndefinedProcedure {
327 name: String,
328 },
329
330 NoRegisteredImplementation {
331 name: String,
332 },
333}
334
335#[derive(Debug, Clone, PartialEq)]
336pub enum RuntimeErrorKind {
337 VariableNotFound {
338 name: String,
339 },
340 VariableIsDataframe {
341 name: String,
342 },
343 VariableIsImmutable {
344 name: String,
345 },
346 BreakOutsideLoop,
347 ContinueOutsideLoop,
348 MaxIterationsExceeded {
349 limit: usize,
350 },
351 UndefinedFunction {
352 name: String,
353 },
354 FieldNotFound {
355 variable: String,
356 field: String,
357 available: Vec<String>,
358 },
359 AppendTargetNotFrame {
360 name: String,
361 },
362 AppendColumnMismatch {
363 name: String,
364 existing: Vec<String>,
365 incoming: Vec<String>,
366 fragment: Fragment,
367 },
368 ExpectedSingleColumn {
369 actual: usize,
370 },
371}
372
373#[derive(Debug, Clone, PartialEq)]
374pub enum NetworkErrorKind {
375 Connection {
376 message: String,
377 },
378 Engine {
379 message: String,
380 },
381 Transport {
382 message: String,
383 },
384 Status {
385 message: String,
386 },
387}
388
389#[derive(Debug, Clone, PartialEq)]
390pub enum AuthErrorKind {
391 AuthenticationFailed {
392 reason: String,
393 },
394 AuthorizationDenied {
395 resource: String,
396 },
397 TokenExpired,
398 InvalidToken,
399}
400
401#[derive(Debug, Clone, PartialEq)]
402pub enum FunctionErrorKind {
403 UnknownFunction,
404 ArityMismatch {
405 expected: usize,
406 actual: usize,
407 },
408 TooManyArguments {
409 max_args: usize,
410 actual: usize,
411 },
412 InvalidArgumentType {
413 index: usize,
414 expected: Vec<Type>,
415 actual: Type,
416 },
417 UndefinedArgument {
418 index: usize,
419 },
420 MissingInput,
421 ExecutionFailed {
422 reason: String,
423 },
424 InternalError {
425 details: String,
426 },
427 GeneratorNotFound,
428}
429
430#[derive(Debug, thiserror::Error)]
431pub enum TypeError {
432 #[error("Cannot apply {operator} operator to {operand_category}")]
433 LogicalOperatorNotApplicable {
434 operator: LogicalOp,
435 operand_category: OperandCategory,
436 fragment: Fragment,
437 },
438
439 #[error("Cannot apply '{operator}' operator to {left} and {right}")]
440 BinaryOperatorNotApplicable {
441 operator: BinaryOp,
442 left: Type,
443 right: Type,
444 fragment: Fragment,
445 },
446
447 #[error("unsupported cast from {from} to {to}")]
448 UnsupportedCast {
449 from: Type,
450 to: Type,
451 fragment: Fragment,
452 },
453
454 #[error("failed to cast to {target}")]
455 CastToNumberFailed {
456 target: Type,
457 fragment: Fragment,
458 cause: Box<TypeError>,
459 },
460
461 #[error("failed to cast to {target}")]
462 CastToTemporalFailed {
463 target: Type,
464 fragment: Fragment,
465 cause: Box<TypeError>,
466 },
467
468 #[error("failed to cast to bool")]
469 CastToBooleanFailed {
470 fragment: Fragment,
471 cause: Box<TypeError>,
472 },
473
474 #[error("failed to cast to {target}")]
475 CastToUuidFailed {
476 target: Type,
477 fragment: Fragment,
478 cause: Box<TypeError>,
479 },
480
481 #[error("failed to cast BLOB to UTF8")]
482 CastBlobToUtf8Failed {
483 fragment: Fragment,
484 cause: Box<TypeError>,
485 },
486
487 #[error("{message}")]
488 ConstraintViolation {
489 kind: ConstraintKind,
490 message: String,
491 fragment: Fragment,
492 },
493
494 #[error("invalid number format")]
495 InvalidNumberFormat {
496 target: Type,
497 fragment: Fragment,
498 },
499
500 #[error("number out of range")]
501 NumberOutOfRange {
502 target: Type,
503 fragment: Fragment,
504 descriptor: Option<NumberOutOfRangeDescriptor>,
505 },
506
507 #[error("NaN not allowed")]
508 NanNotAllowed,
509
510 #[error("too large for precise float conversion")]
511 IntegerPrecisionLoss {
512 shape_type: Type,
513 target: Type,
514 fragment: Fragment,
515 },
516
517 #[error("decimal scale exceeds precision")]
518 DecimalScaleExceedsPrecision {
519 scale: u8,
520 precision: u8,
521 fragment: Fragment,
522 },
523
524 #[error("invalid decimal precision")]
525 DecimalPrecisionInvalid {
526 precision: u8,
527 },
528
529 #[error("invalid boolean format")]
530 InvalidBooleanFormat {
531 fragment: Fragment,
532 },
533
534 #[error("empty boolean value")]
535 EmptyBooleanValue {
536 fragment: Fragment,
537 },
538
539 #[error("invalid boolean")]
540 InvalidNumberBoolean {
541 fragment: Fragment,
542 },
543
544 #[error("{message}")]
545 Temporal {
546 kind: TemporalKind,
547 message: String,
548 fragment: Fragment,
549 },
550
551 #[error("invalid UUID v4 format")]
552 InvalidUuid4Format {
553 fragment: Fragment,
554 },
555
556 #[error("invalid UUID v7 format")]
557 InvalidUuid7Format {
558 fragment: Fragment,
559 },
560
561 #[error("{message}")]
562 BlobEncoding {
563 kind: BlobEncodingKind,
564 message: String,
565 fragment: Fragment,
566 },
567
568 #[error("Serde deserialization error: {message}")]
569 SerdeDeserialize {
570 message: String,
571 },
572
573 #[error("Serde serialization error: {message}")]
574 SerdeSerialize {
575 message: String,
576 },
577
578 #[error("Keycode serialization error: {message}")]
579 SerdeKeycode {
580 message: String,
581 },
582
583 #[error("Array conversion error: {message}")]
584 ArrayConversion {
585 message: String,
586 },
587
588 #[error("UTF-8 conversion error: {message}")]
589 Utf8Conversion {
590 message: String,
591 },
592
593 #[error("Integer conversion error: {message}")]
594 IntegerConversion {
595 message: String,
596 },
597
598 #[error("{message}")]
599 Network {
600 kind: NetworkErrorKind,
601 message: String,
602 },
603
604 #[error("{message}")]
605 Auth {
606 kind: AuthErrorKind,
607 message: String,
608 },
609
610 #[error("dictionary entry ID {value} exceeds maximum {max_value} for type {id_type}")]
611 DictionaryCapacityExceeded {
612 id_type: Type,
613 value: u128,
614 max_value: u128,
615 },
616
617 #[error("{message}")]
618 AssertionFailed {
619 fragment: Fragment,
620 message: String,
621 expression: Option<String>,
622 },
623
624 #[error("{message}")]
625 Function {
626 kind: FunctionErrorKind,
627 message: String,
628 fragment: Fragment,
629 },
630
631 #[error("{message}")]
632 Ast {
633 kind: AstErrorKind,
634 message: String,
635 fragment: Fragment,
636 },
637
638 #[error("{message}")]
639 Runtime {
640 kind: RuntimeErrorKind,
641 message: String,
642 },
643
644 #[error("{message}")]
645 Procedure {
646 kind: ProcedureErrorKind,
647 message: String,
648 fragment: Fragment,
649 },
650}
651
652#[derive(Debug, Clone, PartialEq)]
653pub struct NumberOutOfRangeDescriptor {
654 pub namespace: Option<String>,
655 pub table: Option<String>,
656 pub column: Option<String>,
657 pub column_type: Option<Type>,
658}
659
660impl NumberOutOfRangeDescriptor {
661 pub fn location_string(&self) -> String {
662 match (self.namespace.as_deref(), self.table.as_deref(), self.column.as_deref()) {
663 (Some(s), Some(t), Some(c)) => format!("{}::{}.{}", s, t, c),
664 (Some(s), Some(t), None) => format!("{}::{}", s, t),
665 (None, Some(t), Some(c)) => format!("{}.{}", t, c),
666 (Some(s), None, Some(c)) => format!("{}::{}", s, c),
667 (Some(s), None, None) => s.to_string(),
668 (None, Some(t), None) => t.to_string(),
669 (None, None, Some(c)) => c.to_string(),
670 (None, None, None) => "unknown location".to_string(),
671 }
672 }
673}
674
675#[derive(Debug, PartialEq)]
676pub struct Error(pub Box<Diagnostic>);
677
678impl Deref for Error {
679 type Target = Diagnostic;
680
681 fn deref(&self) -> &Self::Target {
682 &self.0
683 }
684}
685
686impl DerefMut for Error {
687 fn deref_mut(&mut self) -> &mut Self::Target {
688 &mut self.0
689 }
690}
691
692impl Display for Error {
693 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
694 let out = DefaultRenderer::render_string(&self.0);
695 f.write_str(out.as_str())
696 }
697}
698
699impl Error {
700 pub fn diagnostic(self) -> Diagnostic {
701 *self.0
702 }
703}
704
705impl error::Error for Error {}
706
707impl de::Error for Error {
708 fn custom<T: Display>(msg: T) -> Self {
709 TypeError::SerdeDeserialize {
710 message: msg.to_string(),
711 }
712 .into()
713 }
714}
715
716impl ser::Error for Error {
717 fn custom<T: Display>(msg: T) -> Self {
718 TypeError::SerdeSerialize {
719 message: msg.to_string(),
720 }
721 .into()
722 }
723}
724
725impl From<num::TryFromIntError> for Error {
726 fn from(err: num::TryFromIntError) -> Self {
727 TypeError::IntegerConversion {
728 message: err.to_string(),
729 }
730 .into()
731 }
732}
733
734impl From<TryFromSliceError> for Error {
735 fn from(err: TryFromSliceError) -> Self {
736 TypeError::ArrayConversion {
737 message: err.to_string(),
738 }
739 .into()
740 }
741}
742
743impl From<string::FromUtf8Error> for Error {
744 fn from(err: string::FromUtf8Error) -> Self {
745 TypeError::Utf8Conversion {
746 message: err.to_string(),
747 }
748 .into()
749 }
750}
751
752impl From<TypeError> for Error {
753 fn from(err: TypeError) -> Self {
754 Error(Box::new(err.into_diagnostic()))
755 }
756}
757
758impl From<Box<TypeError>> for Error {
759 fn from(err: Box<TypeError>) -> Self {
760 Error(Box::new(err.into_diagnostic()))
761 }
762}
763
764impl From<num::TryFromIntError> for TypeError {
765 fn from(err: num::TryFromIntError) -> Self {
766 TypeError::IntegerConversion {
767 message: err.to_string(),
768 }
769 }
770}
771
772impl From<TryFromSliceError> for TypeError {
773 fn from(err: TryFromSliceError) -> Self {
774 TypeError::ArrayConversion {
775 message: err.to_string(),
776 }
777 }
778}
779
780impl From<string::FromUtf8Error> for TypeError {
781 fn from(err: string::FromUtf8Error) -> Self {
782 TypeError::Utf8Conversion {
783 message: err.to_string(),
784 }
785 }
786}
787
788impl de::Error for TypeError {
789 fn custom<T: Display>(msg: T) -> Self {
790 TypeError::SerdeDeserialize {
791 message: msg.to_string(),
792 }
793 }
794}
795
796impl ser::Error for TypeError {
797 fn custom<T: Display>(msg: T) -> Self {
798 TypeError::SerdeSerialize {
799 message: msg.to_string(),
800 }
801 }
802}