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
22use crate::data::export_v2::manifest::ChunkStatus;
23
24#[derive(Snafu)]
25#[snafu(visibility(pub))]
26#[stack_trace_debug]
27pub enum Error {
28 #[snafu(display("Snapshot not found at '{}'", uri))]
29 SnapshotNotFound {
30 uri: String,
31 #[snafu(implicit)]
32 location: Location,
33 },
34
35 #[snafu(display("Manifest version mismatch: expected {}, found {}", expected, found))]
36 ManifestVersionMismatch {
37 expected: u32,
38 found: u32,
39 #[snafu(implicit)]
40 location: Location,
41 },
42
43 #[snafu(display("Schema '{}' not found in snapshot", schema))]
44 SchemaNotInSnapshot {
45 schema: String,
46 #[snafu(implicit)]
47 location: Location,
48 },
49
50 #[snafu(display("Incomplete snapshot: chunk {} has status {:?}", chunk_id, status))]
51 IncompleteSnapshot {
52 chunk_id: u32,
53 status: ChunkStatus,
54 #[snafu(implicit)]
55 location: Location,
56 },
57
58 #[snafu(display(
59 "Snapshot is inconsistent: chunk {} is marked completed but its file manifest is empty",
60 chunk_id
61 ))]
62 EmptyChunkManifest {
63 chunk_id: u32,
64 #[snafu(implicit)]
65 location: Location,
66 },
67
68 #[snafu(display(
69 "Snapshot is inconsistent: chunk {} for schema '{}' is marked completed but no files were found under '{}'",
70 chunk_id,
71 schema,
72 path
73 ))]
74 MissingChunkData {
75 chunk_id: u32,
76 schema: String,
77 path: String,
78 #[snafu(implicit)]
79 location: Location,
80 },
81
82 #[snafu(display("Chunk {} import failed for schema '{}'", chunk_id, schema))]
83 ChunkImportFailed {
84 chunk_id: u32,
85 schema: String,
86 #[snafu(source)]
87 error: crate::data::export_v2::error::Error,
88 #[snafu(implicit)]
89 location: Location,
90 },
91
92 #[snafu(display("Snapshot storage error"))]
93 SnapshotStorage {
94 #[snafu(source)]
95 error: crate::data::export_v2::error::Error,
96 #[snafu(implicit)]
97 location: Location,
98 },
99
100 #[snafu(display("Database error"))]
101 Database {
102 #[snafu(source)]
103 error: crate::error::Error,
104 #[snafu(implicit)]
105 location: Location,
106 },
107
108 #[snafu(display("Failed to parse import state file"))]
109 ImportStateParse {
110 #[snafu(source)]
111 error: serde_json::Error,
112 #[snafu(implicit)]
113 location: Location,
114 },
115
116 #[snafu(display("Import state I/O failed at '{}': {}", path, error))]
117 ImportStateIo {
118 path: String,
119 #[snafu(source)]
120 error: std::io::Error,
121 #[snafu(implicit)]
122 location: Location,
123 },
124
125 #[snafu(display("Import state is already locked at '{}'", path))]
126 ImportStateLocked {
127 path: String,
128 #[snafu(implicit)]
129 location: Location,
130 },
131
132 #[snafu(display(
133 "Failed to determine import state path for snapshot '{}'. Set HOME, USERPROFILE, or run from a valid current directory.",
134 snapshot_id
135 ))]
136 ImportStatePathUnavailable {
137 snapshot_id: String,
138 #[snafu(implicit)]
139 location: Location,
140 },
141
142 #[snafu(display(
143 "Import state at '{}' does not match current import: {}. Either rerun with matching import arguments, or delete the state file to start over (DDL will be re-executed).",
144 path,
145 reason
146 ))]
147 ImportStateMismatch {
148 path: String,
149 reason: String,
150 #[snafu(implicit)]
151 location: Location,
152 },
153
154 #[cfg(test)]
155 #[snafu(display("Test task failed: {}", message))]
156 TestTaskFailed {
157 message: String,
158 retryable: bool,
159 #[snafu(implicit)]
160 location: Location,
161 },
162
163 #[snafu(display(
164 "Import state references unknown task: chunk {}, schema '{}'",
165 chunk_id,
166 schema
167 ))]
168 ImportStateUnknownTask {
169 chunk_id: u32,
170 schema: String,
171 #[snafu(implicit)]
172 location: Location,
173 },
174
175 #[snafu(display(
176 "Import state at '{}' is not ready for data import: DDL has not been marked completed",
177 path
178 ))]
179 ImportStateDdlIncomplete {
180 path: String,
181 #[snafu(implicit)]
182 location: Location,
183 },
184}
185
186pub type Result<T> = std::result::Result<T, Error>;
187
188impl ErrorExt for Error {
189 fn status_code(&self) -> StatusCode {
190 match self {
191 Error::SnapshotNotFound { .. }
192 | Error::SchemaNotInSnapshot { .. }
193 | Error::ManifestVersionMismatch { .. }
194 | Error::IncompleteSnapshot { .. }
195 | Error::EmptyChunkManifest { .. }
196 | Error::MissingChunkData { .. } => StatusCode::InvalidArguments,
197 Error::ImportStatePathUnavailable { .. }
198 | Error::ImportStateUnknownTask { .. }
199 | Error::ImportStateDdlIncomplete { .. } => StatusCode::Unexpected,
200 Error::ImportStateMismatch { .. } => StatusCode::InvalidArguments,
201 #[cfg(test)]
202 Error::TestTaskFailed { retryable, .. } => {
203 if *retryable {
204 StatusCode::StorageUnavailable
205 } else {
206 StatusCode::InvalidArguments
207 }
208 }
209 Error::Database { error, .. } => error.status_code(),
210 Error::SnapshotStorage { error, .. } | Error::ChunkImportFailed { error, .. } => {
211 error.status_code()
212 }
213 Error::ImportStateParse { .. } => StatusCode::Internal,
214 Error::ImportStateIo { .. } => StatusCode::StorageUnavailable,
215 Error::ImportStateLocked { .. } => StatusCode::IllegalState,
216 }
217 }
218
219 fn as_any(&self) -> &dyn Any {
220 self
221 }
222}