1use std::any::Any;
16
17use common_error::ext::ErrorExt;
18use common_error::status_code::StatusCode;
19use common_macro::stack_trace_debug;
20use snafu::{Location, Snafu};
21
22#[derive(Snafu)]
23#[snafu(visibility(pub))]
24#[stack_trace_debug]
25pub enum Error {
26 #[snafu(display("Invalid URI '{}': {}", uri, reason))]
27 InvalidUri {
28 uri: String,
29 reason: String,
30 #[snafu(implicit)]
31 location: Location,
32 },
33
34 #[snafu(display("Unsupported storage scheme: {}", scheme))]
35 UnsupportedScheme {
36 scheme: String,
37 #[snafu(implicit)]
38 location: Location,
39 },
40
41 #[snafu(display("Storage operation '{}' failed", operation))]
42 StorageOperation {
43 operation: String,
44 #[snafu(source)]
45 error: object_store::Error,
46 #[snafu(implicit)]
47 location: Location,
48 },
49
50 #[snafu(display("Failed to parse manifest"))]
51 ManifestParse {
52 #[snafu(source)]
53 error: serde_json::Error,
54 #[snafu(implicit)]
55 location: Location,
56 },
57
58 #[snafu(display("Failed to serialize manifest"))]
59 ManifestSerialize {
60 #[snafu(source)]
61 error: serde_json::Error,
62 #[snafu(implicit)]
63 location: Location,
64 },
65
66 #[snafu(display("Failed to decode text file as UTF-8"))]
67 TextDecode {
68 #[snafu(source)]
69 error: std::string::FromUtf8Error,
70 #[snafu(implicit)]
71 location: Location,
72 },
73
74 #[snafu(display("I/O error while {}: {}", operation, error))]
75 Io {
76 operation: &'static str,
77 error: std::io::Error,
78 #[snafu(implicit)]
79 location: Location,
80 },
81
82 #[snafu(display(
83 "Cannot resume snapshot with a different schema_only mode (existing: {}, requested: {}). Use --force to recreate.",
84 existing_schema_only,
85 requested_schema_only
86 ))]
87 SchemaOnlyModeMismatch {
88 existing_schema_only: bool,
89 requested_schema_only: bool,
90 #[snafu(implicit)]
91 location: Location,
92 },
93
94 #[snafu(display(
95 "Cannot resume snapshot with different {} (existing: {}, requested: {}). Use --force to recreate.",
96 field,
97 existing,
98 requested
99 ))]
100 ResumeConfigMismatch {
101 field: String,
102 existing: String,
103 requested: String,
104 #[snafu(implicit)]
105 location: Location,
106 },
107
108 #[snafu(display("Failed to parse time: invalid format: {}", input))]
109 TimeParseInvalidFormat {
110 input: String,
111 #[snafu(implicit)]
112 location: Location,
113 },
114
115 #[snafu(display("Failed to parse time: end_time is before start_time"))]
116 TimeParseEndBeforeStart {
117 #[snafu(implicit)]
118 location: Location,
119 },
120
121 #[snafu(display(
122 "chunk_time_window requires both --start-time and --end-time to be specified"
123 ))]
124 ChunkTimeWindowRequiresBounds {
125 #[snafu(implicit)]
126 location: Location,
127 },
128
129 #[snafu(display("--schema-only cannot be used with data export arguments: {}", args))]
130 SchemaOnlyArgsNotAllowed {
131 args: String,
132 #[snafu(implicit)]
133 location: Location,
134 },
135
136 #[snafu(display("Empty result from query"))]
137 EmptyResult {
138 #[snafu(implicit)]
139 location: Location,
140 },
141
142 #[snafu(display("Unexpected value type in query result"))]
143 UnexpectedValueType {
144 #[snafu(implicit)]
145 location: Location,
146 },
147
148 #[snafu(display("Database error"))]
149 Database {
150 #[snafu(source)]
151 error: crate::error::Error,
152 #[snafu(implicit)]
153 location: Location,
154 },
155
156 #[snafu(display("Snapshot not found at '{}'", uri))]
157 SnapshotNotFound {
158 uri: String,
159 #[snafu(implicit)]
160 location: Location,
161 },
162
163 #[snafu(display("Schema '{}' not found in catalog '{}'", schema, catalog))]
164 SchemaNotFound {
165 catalog: String,
166 schema: String,
167 #[snafu(implicit)]
168 location: Location,
169 },
170
171 #[snafu(display("Failed to parse URL"))]
172 UrlParse {
173 #[snafu(source)]
174 error: url::ParseError,
175 #[snafu(implicit)]
176 location: Location,
177 },
178
179 #[snafu(display("Failed to build object store"))]
180 BuildObjectStore {
181 #[snafu(source)]
182 error: object_store::Error,
183 #[snafu(implicit)]
184 location: Location,
185 },
186
187 #[snafu(display("Manifest version mismatch: expected {}, found {}", expected, found))]
188 ManifestVersionMismatch {
189 expected: u32,
190 found: u32,
191 #[snafu(implicit)]
192 location: Location,
193 },
194
195 #[snafu(display(
196 "Snapshot verification failed: {} error(s), {} warning(s)",
197 errors,
198 warnings
199 ))]
200 SnapshotVerifyFailed {
201 errors: usize,
202 warnings: usize,
203 #[snafu(implicit)]
204 location: Location,
205 },
206}
207
208pub type Result<T> = std::result::Result<T, Error>;
209
210impl ErrorExt for Error {
211 fn status_code(&self) -> StatusCode {
212 match self {
213 Error::InvalidUri { .. }
214 | Error::UnsupportedScheme { .. }
215 | Error::SchemaOnlyModeMismatch { .. }
216 | Error::ResumeConfigMismatch { .. }
217 | Error::ManifestVersionMismatch { .. }
218 | Error::SchemaOnlyArgsNotAllowed { .. }
219 | Error::SnapshotVerifyFailed { .. } => StatusCode::InvalidArguments,
220 Error::TimeParseInvalidFormat { .. }
221 | Error::TimeParseEndBeforeStart { .. }
222 | Error::ChunkTimeWindowRequiresBounds { .. } => StatusCode::InvalidArguments,
223
224 Error::StorageOperation { .. }
225 | Error::ManifestParse { .. }
226 | Error::ManifestSerialize { .. }
227 | Error::TextDecode { .. }
228 | Error::BuildObjectStore { .. } => StatusCode::StorageUnavailable,
229
230 Error::EmptyResult { .. }
231 | Error::UnexpectedValueType { .. }
232 | Error::UrlParse { .. } => StatusCode::Internal,
233
234 Error::Io { .. } => StatusCode::External,
235
236 Error::Database { error, .. } => error.status_code(),
237
238 Error::SnapshotNotFound { .. } => StatusCode::InvalidArguments,
239 Error::SchemaNotFound { .. } => StatusCode::DatabaseNotFound,
240 }
241 }
242
243 fn as_any(&self) -> &dyn Any {
244 self
245 }
246}