1#[derive(Debug)]
5pub enum QailError {
6 Parse {
8 position: usize,
10 message: String,
12 },
13
14 InvalidAction(String),
16
17 MissingSymbol {
19 symbol: &'static str,
21 description: &'static str,
23 },
24
25 InvalidOperator(String),
27
28 InvalidValue(String),
30
31 Database(String),
33
34 Connection(String),
36
37 Execution(String),
39
40 Validation(String),
42
43 Config(String),
45
46 Io(std::io::Error),
48}
49
50impl QailError {
51 pub fn parse(position: usize, message: impl Into<String>) -> Self {
53 Self::Parse {
54 position,
55 message: message.into(),
56 }
57 }
58
59 pub fn missing(symbol: &'static str, description: &'static str) -> Self {
61 Self::MissingSymbol {
62 symbol,
63 description,
64 }
65 }
66}
67
68impl std::fmt::Display for QailError {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 match self {
71 Self::Parse { position, message } => {
72 write!(f, "Parse error at position {position}: {message}")
73 }
74 Self::InvalidAction(action) => {
75 write!(
76 f,
77 "Invalid action: '{action}'. Expected: get, set, del, or add"
78 )
79 }
80 Self::MissingSymbol {
81 symbol,
82 description,
83 } => {
84 write!(f, "Missing required symbol: {symbol} ({description})")
85 }
86 Self::InvalidOperator(op) => write!(f, "Invalid operator: '{op}'"),
87 Self::InvalidValue(value) => write!(f, "Invalid value: {value}"),
88 Self::Database(msg) => write!(f, "Database error: {msg}"),
89 Self::Connection(msg) => write!(f, "Connection error: {msg}"),
90 Self::Execution(msg) => write!(f, "Execution error: {msg}"),
91 Self::Validation(msg) => write!(f, "Validation error: {msg}"),
92 Self::Config(msg) => write!(f, "Configuration error: {msg}"),
93 Self::Io(err) => write!(f, "IO error: {err}"),
94 }
95 }
96}
97
98impl std::error::Error for QailError {
99 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
100 match self {
101 Self::Io(err) => Some(err),
102 _ => None,
103 }
104 }
105}
106
107impl From<std::io::Error> for QailError {
108 fn from(value: std::io::Error) -> Self {
109 Self::Io(value)
110 }
111}
112
113pub type QailResult<T> = Result<T, QailError>;
115
116#[derive(Debug, Clone, PartialEq, Eq)]
118pub enum QailBuildError {
119 RlsInsertRequiresExplicitColumns {
121 table: String,
123 tenant_column: String,
125 },
126
127 RlsTenantColumnMutationDenied {
129 table: String,
131 tenant_column: String,
133 },
134
135 RlsMergeSourceTenantProjectionRequired {
137 table: String,
139 tenant_column: String,
141 },
142
143 RelationRegistryLock(String),
145
146 AmbiguousRelation {
148 from_table: String,
150 to_table: String,
152 foreign_key_count: usize,
154 },
155
156 RelationNotFound {
158 from_table: String,
160 to_table: String,
162 },
163}
164
165impl std::fmt::Display for QailBuildError {
166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167 match self {
168 Self::RlsInsertRequiresExplicitColumns {
169 table,
170 tenant_column,
171 } => write!(
172 f,
173 "with_rls requires explicit columns for positional INSERT payloads on table '{table}' (tenant column '{tenant_column}')"
174 ),
175 Self::RlsTenantColumnMutationDenied {
176 table,
177 tenant_column,
178 } => write!(
179 f,
180 "with_rls rejects tenant column mutation on table '{table}' (tenant column '{tenant_column}')"
181 ),
182 Self::RlsMergeSourceTenantProjectionRequired {
183 table,
184 tenant_column,
185 } => write!(
186 f,
187 "with_rls requires MERGE query sources for table '{table}' to project tenant column '{tenant_column}'"
188 ),
189 Self::RelationRegistryLock(msg) => write!(f, "Relation registry lock error: {msg}"),
190 Self::AmbiguousRelation {
191 from_table,
192 to_table,
193 foreign_key_count,
194 } => write!(
195 f,
196 "Ambiguous relation between '{from_table}' and '{to_table}': {foreign_key_count} foreign keys registered. Use an explicit join condition."
197 ),
198 Self::RelationNotFound {
199 from_table,
200 to_table,
201 } => write!(
202 f,
203 "No relation found between '{from_table}' and '{to_table}'. Define a ref: in schema.qail or use load_schema_relations() first."
204 ),
205 }
206 }
207}
208
209impl std::error::Error for QailBuildError {}
210
211pub type QailBuildResult<T> = Result<T, QailBuildError>;
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_error_display() {
220 let err = QailError::parse(5, "unexpected character");
221 assert_eq!(
222 err.to_string(),
223 "Parse error at position 5: unexpected character"
224 );
225 }
226}