1use std::fmt::{Debug, Display};
2use std::sync::Arc;
3
4pub struct CrawlerError {
5 inner: Box<ErrorKind>,
6}
7
8pub type CrawlerResult<T> = std::result::Result<T, CrawlerError>;
9
10enum ErrorKind {
12 ArraySize {
14 key: String,
16 json: Arc<String>,
20 min_elements: usize,
22 },
23 PathNotFoundInArray {
25 key: String,
27 json: Arc<String>,
31 target_path: String,
34 },
35 PathsNotFound {
37 key: String,
39 json: Arc<String>,
43 target_paths: Vec<String>,
45 },
46 Parsing {
50 key: String,
52 json: Arc<String>,
56 target: ParseTarget,
58 message: Option<String>,
61 },
62 Navigation {
64 key: String,
66 json: Arc<String>,
70 },
71 MultipleParseError {
73 key: String,
74 json: Arc<String>,
75 messages: Vec<String>,
76 },
77}
78
79#[derive(Debug, Clone)]
81pub(crate) enum ParseTarget {
82 Array,
83 Other(String),
84}
85
86impl std::fmt::Display for ParseTarget {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self {
89 ParseTarget::Array => write!(f, "Array"),
90 ParseTarget::Other(t) => write!(f, "{t}"),
91 }
92 }
93}
94
95impl std::error::Error for CrawlerError {}
96
97impl CrawlerError {
98 pub fn get_json_and_key(&self) -> (String, &String) {
100 match self.inner.as_ref() {
101 ErrorKind::Navigation { json, key } => (json.to_string(), key),
102 ErrorKind::Parsing { json, key, .. } => (json.to_string(), key),
103 ErrorKind::PathNotFoundInArray { key, json, .. } => (json.to_string(), key),
104 ErrorKind::PathsNotFound { key, json, .. } => (json.to_string(), key),
105 ErrorKind::ArraySize { key, json, .. } => (json.to_string(), key),
106 ErrorKind::MultipleParseError { key, json, .. } => (json.to_string(), key),
107 }
108 }
109 pub(crate) fn multiple_parse_error(
110 key: impl Into<String>,
111 json: Arc<String>,
112 errors: Vec<CrawlerError>,
113 ) -> Self {
114 let messages = errors.into_iter().map(|e| format!("{e}")).collect();
115 Self {
116 inner: Box::new(ErrorKind::MultipleParseError {
117 key: key.into(),
118 json,
119 messages,
120 }),
121 }
122 }
123 pub(crate) fn navigation(key: impl Into<String>, json: Arc<String>) -> Self {
124 Self {
125 inner: Box::new(ErrorKind::Navigation {
126 key: key.into(),
127 json,
128 }),
129 }
130 }
131 pub(crate) fn array_size(
132 key: impl Into<String>,
133 json: Arc<String>,
134 min_elements: usize,
135 ) -> Self {
136 let key = key.into();
137 Self {
138 inner: Box::new(ErrorKind::ArraySize {
139 key,
140 json,
141 min_elements,
142 }),
143 }
144 }
145 pub(crate) fn path_not_found_in_array(
146 key: impl Into<String>,
147 json: Arc<String>,
148 target_path: impl Into<String>,
149 ) -> Self {
150 let key = key.into();
151 let target_path = target_path.into();
152 Self {
153 inner: Box::new(ErrorKind::PathNotFoundInArray {
154 key,
155 json,
156 target_path,
157 }),
158 }
159 }
160 pub(crate) fn paths_not_found(
161 key: impl Into<String>,
162 json: Arc<String>,
163 target_paths: Vec<String>,
164 ) -> Self {
165 let key = key.into();
166 Self {
167 inner: Box::new(ErrorKind::PathsNotFound {
168 key,
169 json,
170 target_paths,
171 }),
172 }
173 }
174 pub(crate) fn parsing<S: Into<String>>(
175 key: S,
176 json: Arc<String>,
177 target: ParseTarget,
178 message: Option<String>,
179 ) -> Self {
180 Self {
181 inner: Box::new(ErrorKind::Parsing {
182 key: key.into(),
183 json,
184 target,
185 message,
186 }),
187 }
188 }
189}
190impl Display for ErrorKind {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 match self {
193 ErrorKind::PathsNotFound {
194 key, target_paths, ..
195 } => write!(
196 f,
197 "Expected {key} to contain one of the following paths: {:?}",
198 target_paths
199 ),
200 ErrorKind::PathNotFoundInArray {
201 key, target_path, ..
202 } => write!(f, "Expected {key} to contain a {target_path}"),
203 ErrorKind::Navigation { key, json: _ } => {
204 write!(f, "Key {key} not found in Api response.")
205 }
206 ErrorKind::ArraySize {
207 key,
208 json: _,
209 min_elements,
210 } => {
211 write!(
212 f,
213 "Expected {key} to contain at least {min_elements} elements."
214 )
215 }
216 ErrorKind::Parsing {
217 key,
218 json: _,
219 target,
220 message,
221 } => write!(
222 f,
223 "Error {}. Unable to parse into {target} at {key}",
224 message.as_deref().unwrap_or_default()
225 ),
226 ErrorKind::MultipleParseError { key, json: _, messages } => write!(f,"Expected one of the parsing functions at {key} to succeed, but all failed with the following errors: {:?}",messages),
227 }
228 }
229}
230
231impl Debug for CrawlerError {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 Display::fmt(&*self.inner, f)
237 }
238}
239impl Display for CrawlerError {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 Display::fmt(&*self.inner, f)
242 }
243}