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(
75 "Cannot resume snapshot with a different schema_only mode (existing: {}, requested: {}). Use --force to recreate.",
76 existing_schema_only,
77 requested_schema_only
78 ))]
79 SchemaOnlyModeMismatch {
80 existing_schema_only: bool,
81 requested_schema_only: bool,
82 #[snafu(implicit)]
83 location: Location,
84 },
85
86 #[snafu(display(
87 "Cannot resume snapshot with different {} (existing: {}, requested: {}). Use --force to recreate.",
88 field,
89 existing,
90 requested
91 ))]
92 ResumeConfigMismatch {
93 field: String,
94 existing: String,
95 requested: String,
96 #[snafu(implicit)]
97 location: Location,
98 },
99
100 #[snafu(display("Failed to parse time: invalid format: {}", input))]
101 TimeParseInvalidFormat {
102 input: String,
103 #[snafu(implicit)]
104 location: Location,
105 },
106
107 #[snafu(display("Failed to parse time: end_time is before start_time"))]
108 TimeParseEndBeforeStart {
109 #[snafu(implicit)]
110 location: Location,
111 },
112
113 #[snafu(display(
114 "chunk_time_window requires both --start-time and --end-time to be specified"
115 ))]
116 ChunkTimeWindowRequiresBounds {
117 #[snafu(implicit)]
118 location: Location,
119 },
120
121 #[snafu(display("--schema-only cannot be used with data export arguments: {}", args))]
122 SchemaOnlyArgsNotAllowed {
123 args: String,
124 #[snafu(implicit)]
125 location: Location,
126 },
127
128 #[snafu(display("Empty result from query"))]
129 EmptyResult {
130 #[snafu(implicit)]
131 location: Location,
132 },
133
134 #[snafu(display("Unexpected value type in query result"))]
135 UnexpectedValueType {
136 #[snafu(implicit)]
137 location: Location,
138 },
139
140 #[snafu(display("Database error"))]
141 Database {
142 #[snafu(source)]
143 error: crate::error::Error,
144 #[snafu(implicit)]
145 location: Location,
146 },
147
148 #[snafu(display("Snapshot not found at '{}'", uri))]
149 SnapshotNotFound {
150 uri: String,
151 #[snafu(implicit)]
152 location: Location,
153 },
154
155 #[snafu(display("Schema '{}' not found in catalog '{}'", schema, catalog))]
156 SchemaNotFound {
157 catalog: String,
158 schema: String,
159 #[snafu(implicit)]
160 location: Location,
161 },
162
163 #[snafu(display("Failed to parse URL"))]
164 UrlParse {
165 #[snafu(source)]
166 error: url::ParseError,
167 #[snafu(implicit)]
168 location: Location,
169 },
170
171 #[snafu(display("Failed to build object store"))]
172 BuildObjectStore {
173 #[snafu(source)]
174 error: object_store::Error,
175 #[snafu(implicit)]
176 location: Location,
177 },
178
179 #[snafu(display("Manifest version mismatch: expected {}, found {}", expected, found))]
180 ManifestVersionMismatch {
181 expected: u32,
182 found: u32,
183 #[snafu(implicit)]
184 location: Location,
185 },
186}
187
188pub type Result<T> = std::result::Result<T, Error>;
189
190impl ErrorExt for Error {
191 fn status_code(&self) -> StatusCode {
192 match self {
193 Error::InvalidUri { .. }
194 | Error::UnsupportedScheme { .. }
195 | Error::SchemaOnlyModeMismatch { .. }
196 | Error::ResumeConfigMismatch { .. }
197 | Error::ManifestVersionMismatch { .. }
198 | Error::SchemaOnlyArgsNotAllowed { .. } => StatusCode::InvalidArguments,
199 Error::TimeParseInvalidFormat { .. }
200 | Error::TimeParseEndBeforeStart { .. }
201 | Error::ChunkTimeWindowRequiresBounds { .. } => StatusCode::InvalidArguments,
202
203 Error::StorageOperation { .. }
204 | Error::ManifestParse { .. }
205 | Error::ManifestSerialize { .. }
206 | Error::TextDecode { .. }
207 | Error::BuildObjectStore { .. } => StatusCode::StorageUnavailable,
208
209 Error::EmptyResult { .. }
210 | Error::UnexpectedValueType { .. }
211 | Error::UrlParse { .. } => StatusCode::Internal,
212
213 Error::Database { error, .. } => error.status_code(),
214
215 Error::SnapshotNotFound { .. } => StatusCode::InvalidArguments,
216 Error::SchemaNotFound { .. } => StatusCode::DatabaseNotFound,
217 }
218 }
219
220 fn as_any(&self) -> &dyn Any {
221 self
222 }
223}