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