1use std::path::PathBuf;
4
5use thiserror::Error;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct SourceLocation {
10 pub file: PathBuf,
12 pub line: usize,
14 pub column: usize,
16}
17
18impl SourceLocation {
19 pub const fn new(file: PathBuf, line: usize, column: usize) -> Self {
21 Self { file, line, column }
22 }
23}
24
25impl std::fmt::Display for SourceLocation {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 write!(f, "{}:{}:{}", self.file.display(), self.line, self.column)
28 }
29}
30
31#[derive(Debug, Error)]
33pub enum ParseError {
34 #[error("I/O error reading {path}: {source}")]
36 Io {
37 path: PathBuf,
39 #[source]
41 source: std::io::Error,
42 },
43
44 #[error("pg_query parse error at {location}: {message}")]
46 PgQuery {
47 location: SourceLocation,
49 message: String,
51 },
52
53 #[error(
55 "{location}: {kind} is not supported in pgevolve v0.1 — \
56 see docs §2 for the v0.1 object-kind list; expected to land in a later phase"
57 )]
58 UnsupportedObjectKind {
59 location: SourceLocation,
61 kind: &'static str,
63 },
64
65 #[error(
68 "{location}: object name must be schema-qualified, or the file must declare \
69 `-- @pgevolve schema=<name>`"
70 )]
71 UnqualifiedName {
72 location: SourceLocation,
74 },
75
76 #[error("{location}: {message}")]
78 Structural {
79 location: SourceLocation,
81 message: String,
83 },
84
85 #[error("{location}: {source}")]
87 Ir {
88 location: SourceLocation,
90 #[source]
92 source: crate::ir::IrError,
93 },
94
95 #[error("{location}: invalid pgevolve directive: {message}")]
97 InvalidDirective {
98 location: SourceLocation,
100 message: String,
102 },
103
104 #[error("duplicate object {qname} defined at {first} and {second}")]
106 DuplicateObject {
107 qname: String,
109 first: SourceLocation,
111 second: SourceLocation,
113 },
114
115 #[error("AST resolution failed:\n{}", format_resolution_errors(.0))]
117 AstResolution(Vec<crate::parse::ast_resolution::AstResolutionError>),
118
119 #[error("{0}")]
122 AstCanon(crate::parse::ast_canon::AstCanonError),
123
124 #[error("invalid identifier {0:?}: {1}")]
126 InvalidIdentifier(String, String),
127
128 #[error("{1}: publication {0:?} declared more than once")]
131 DuplicatePublication(crate::identifier::Identifier, SourceLocation),
132
133 #[error(
135 "{1}: publication {0:?}: FOR ALL TABLES cannot be combined with \
136 FOR TABLE or FOR TABLES IN SCHEMA"
137 )]
138 PublicationAllTablesWithObjects(crate::identifier::Identifier, SourceLocation),
139
140 #[error("{1}: publication {0:?}: malformed publication object spec node")]
142 PublicationObjectMalformed(crate::identifier::Identifier, SourceLocation),
143
144 #[error(
146 "{1}: publication {0:?}: FOR TABLES IN CURRENT SCHEMA is not supported \
147 (not declarative; use explicit schema names)"
148 )]
149 PublicationCurrentSchemaForm(crate::identifier::Identifier, SourceLocation),
150
151 #[error(
153 "{2}: publication {1:?}: unknown publication object type {0} \
154 (expected 1=TABLE, 2=TABLES IN SCHEMA, 3=CUR_SCHEMA)"
155 )]
156 UnknownPublicationObjectType(i32, crate::identifier::Identifier, SourceLocation),
157
158 #[error("{1}: publication {0:?}: table must be schema-qualified")]
160 UnqualifiedPublicationTable(crate::identifier::Identifier, SourceLocation),
161
162 #[error("{3}: publication {0:?} table {1}: row filter parse error: {2}")]
164 PublicationFilterParse(
165 crate::identifier::Identifier,
166 crate::identifier::QualifiedName,
167 String,
168 SourceLocation,
169 ),
170
171 #[error("{1}: publication {0:?}: malformed publication option node")]
173 PublicationOptionMalformed(crate::identifier::Identifier, SourceLocation),
174
175 #[error("{2}: publication {1:?}: unknown publication option {0:?}")]
177 UnknownPublicationOption(String, crate::identifier::Identifier, SourceLocation),
178
179 #[error(
181 "{2}: publication {1:?}: unknown publish kind {0:?} \
182 (valid: insert, update, delete, truncate)"
183 )]
184 UnknownPublishKind(String, crate::identifier::Identifier, SourceLocation),
185
186 #[error("{1}: publication {0:?}: empty publish list — at least one DML kind required")]
188 EmptyPublishBitset(crate::identifier::Identifier, SourceLocation),
189
190 #[error(
192 "{1}: publication {0:?}: no scope clause — add FOR ALL TABLES, \
193 FOR TABLE ..., or FOR TABLES IN SCHEMA ..."
194 )]
195 EmptyPublicationScope(crate::identifier::Identifier, SourceLocation),
196
197 #[error("{1}: publication {0:?}: RENAME is not supported in pgevolve")]
199 PublicationRenameNotSupported(crate::identifier::Identifier, SourceLocation),
200
201 #[error("{1}: publication {0:?}: ALTER PUBLICATION before CREATE PUBLICATION")]
203 AlterPublicationBeforeCreate(crate::identifier::Identifier, SourceLocation),
204}
205
206fn format_resolution_errors(errs: &[crate::parse::ast_resolution::AstResolutionError]) -> String {
207 let mut s = String::new();
208 for (i, e) in errs.iter().enumerate() {
209 if i > 0 {
210 s.push('\n');
211 }
212 s.push_str(" - ");
213 s.push_str(&e.to_string());
214 }
215 s
216}