Skip to main content

polyglot_sql/
error.rs

1//! Error types for polyglot-sql
2
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6/// The result type for polyglot operations
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Errors that can occur during SQL parsing and generation
10#[derive(Debug, Error)]
11pub enum Error {
12    /// Error during tokenization
13    #[error("Tokenization error at line {line}, column {column}: {message}")]
14    Tokenize {
15        message: String,
16        line: usize,
17        column: usize,
18    },
19
20    /// Error during parsing
21    #[error("Parse error: {0}")]
22    Parse(String),
23
24    /// Error during SQL generation
25    #[error("Generation error: {0}")]
26    Generate(String),
27
28    /// Unsupported feature for the target dialect
29    #[error("Unsupported: {feature} is not supported in {dialect}")]
30    Unsupported { feature: String, dialect: String },
31
32    /// Invalid SQL syntax
33    #[error("Syntax error at line {line}, column {column}: {message}")]
34    Syntax {
35        message: String,
36        line: usize,
37        column: usize,
38    },
39
40    /// Internal error (should not happen in normal usage)
41    #[error("Internal error: {0}")]
42    Internal(String),
43}
44
45impl Error {
46    /// Create a tokenization error
47    pub fn tokenize(message: impl Into<String>, line: usize, column: usize) -> Self {
48        Error::Tokenize {
49            message: message.into(),
50            line,
51            column,
52        }
53    }
54
55    /// Create a parse error
56    pub fn parse(message: impl Into<String>) -> Self {
57        Error::Parse(message.into())
58    }
59
60    /// Create a generation error
61    pub fn generate(message: impl Into<String>) -> Self {
62        Error::Generate(message.into())
63    }
64
65    /// Create an unsupported feature error
66    pub fn unsupported(feature: impl Into<String>, dialect: impl Into<String>) -> Self {
67        Error::Unsupported {
68            feature: feature.into(),
69            dialect: dialect.into(),
70        }
71    }
72
73    /// Create a syntax error
74    pub fn syntax(message: impl Into<String>, line: usize, column: usize) -> Self {
75        Error::Syntax {
76            message: message.into(),
77            line,
78            column,
79        }
80    }
81
82    /// Create an internal error
83    pub fn internal(message: impl Into<String>) -> Self {
84        Error::Internal(message.into())
85    }
86}
87
88/// Severity level for validation errors
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
90#[serde(rename_all = "lowercase")]
91pub enum ValidationSeverity {
92    /// An error that prevents the query from being valid
93    Error,
94    /// A warning about potential issues
95    Warning,
96}
97
98/// A single validation error or warning
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct ValidationError {
101    /// The error/warning message
102    pub message: String,
103    /// Line number where the error occurred (1-based)
104    pub line: Option<usize>,
105    /// Column number where the error occurred (1-based)
106    pub column: Option<usize>,
107    /// Severity of the validation issue
108    pub severity: ValidationSeverity,
109    /// Error code (e.g., "E001", "W001")
110    pub code: String,
111}
112
113impl ValidationError {
114    /// Create a new validation error
115    pub fn error(message: impl Into<String>, code: impl Into<String>) -> Self {
116        Self {
117            message: message.into(),
118            line: None,
119            column: None,
120            severity: ValidationSeverity::Error,
121            code: code.into(),
122        }
123    }
124
125    /// Create a new validation warning
126    pub fn warning(message: impl Into<String>, code: impl Into<String>) -> Self {
127        Self {
128            message: message.into(),
129            line: None,
130            column: None,
131            severity: ValidationSeverity::Warning,
132            code: code.into(),
133        }
134    }
135
136    /// Set the line number
137    pub fn with_line(mut self, line: usize) -> Self {
138        self.line = Some(line);
139        self
140    }
141
142    /// Set the column number
143    pub fn with_column(mut self, column: usize) -> Self {
144        self.column = Some(column);
145        self
146    }
147
148    /// Set both line and column
149    pub fn with_location(mut self, line: usize, column: usize) -> Self {
150        self.line = Some(line);
151        self.column = Some(column);
152        self
153    }
154}
155
156/// Result of validating SQL
157#[derive(Debug, Serialize, Deserialize)]
158pub struct ValidationResult {
159    /// Whether the SQL is valid (no errors, warnings are allowed)
160    pub valid: bool,
161    /// List of validation errors and warnings
162    pub errors: Vec<ValidationError>,
163}
164
165impl ValidationResult {
166    /// Create a successful validation result
167    pub fn success() -> Self {
168        Self {
169            valid: true,
170            errors: Vec::new(),
171        }
172    }
173
174    /// Create a validation result with errors
175    pub fn with_errors(errors: Vec<ValidationError>) -> Self {
176        let has_errors = errors
177            .iter()
178            .any(|e| e.severity == ValidationSeverity::Error);
179        Self {
180            valid: !has_errors,
181            errors,
182        }
183    }
184
185    /// Add an error to the result
186    pub fn add_error(&mut self, error: ValidationError) {
187        if error.severity == ValidationSeverity::Error {
188            self.valid = false;
189        }
190        self.errors.push(error);
191    }
192}