1use alloc::borrow::Cow;
2use core::panic::Location;
3
4use crate::{
5 Config, GErrDefault, IdSource, gerr,
6 gerr::{ErrorLocation, GErr, Source},
7 gerr_source::{DataSource, GErrSource},
8 gerr_view::GErrView,
9};
10
11#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
13pub struct DisplayJsonData {
14 pub id: Option<serde_json::Value>,
16 pub code: Option<String>,
18 pub message: String,
20 pub tags: Option<Vec<String>>,
22 pub data: Option<serde_json::Value>,
24 pub help: Option<String>,
26 pub causes: Option<Vec<DisplayCausesJsonData>>,
28}
29
30#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
32pub struct DisplayCausesJsonData {
33 pub message: String,
35 pub caused_by: Option<Vec<DisplayCausesJsonData>>,
37}
38
39#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
41pub struct JsonData {
42 pub id: Option<serde_json::Value>,
44 pub code: Option<String>,
46 pub message: String,
48 pub tags: Option<Vec<String>>,
50 pub data: Option<serde_json::Value>,
52 pub location: Option<LocationJsonData>,
54 pub sources: Option<Vec<SourceJsonData>>,
56 pub help: Option<String>,
58
59 pub backtrace: Option<String>,
61}
62
63#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize)]
65pub struct LocationJsonData {
66 pub file: String,
68 pub line: u32,
70 pub column: u32,
72}
73
74#[derive(Debug, Clone, ::serde::Serialize, ::serde::Deserialize, Default)]
76pub struct SourceJsonData {
77 pub id: Option<serde_json::Value>,
79 pub code: Option<String>,
81 pub message: String,
83 pub tags: Option<Vec<String>>,
85 pub data: Option<serde_json::Value>,
87 pub location: Option<LocationJsonData>,
89 pub sources: Option<Vec<SourceJsonData>>,
91 pub help: Option<String>,
93}
94
95impl<'a, C: Config, D> From<&GErrView<'a, C, D>> for JsonData
96where
97 C::Id: ::serde::Serialize,
98 D: ::serde::Serialize,
99{
100 fn from(err: &GErrView<'a, C, D>) -> Self {
101 JsonData {
102 id: err
103 .id
104 .map(|id| ::serde_json::to_value(id).unwrap_or_default()),
105 code: err.code.map(|s| s.into()),
106 message: err.message.into(),
107 tags: err.tags.map(|t| t.iter().map(|s| s.to_string()).collect()),
108 data: err
109 .data
110 .map(|d| serde_json::to_value(d).unwrap_or_default()),
111 help: err.help.map(Into::into),
112 location: Some(LocationJsonData {
113 file: err.location.file.to_string(),
114 line: err.location.line,
115 column: err.location.column,
116 }),
117 sources: err.sources.map(|s| s.iter().map(|i| i.into()).collect()),
118 #[cfg(not(feature = "backtrace"))]
119 backtrace: None,
120 #[cfg(feature = "backtrace")]
121 backtrace: match err.backtrace.status() {
122 std::backtrace::BacktraceStatus::Disabled => Some("<disabled>".into()),
123 std::backtrace::BacktraceStatus::Captured => Some(err.backtrace.to_string()),
124 _ => Some("<unsupported>".into()),
125 },
126 }
127 }
128}
129
130impl<'a, C: Config, D> From<&GErrView<'a, C, D>> for DisplayJsonData
131where
132 C::Id: ::serde::Serialize,
133 D: ::serde::Serialize,
134{
135 fn from(err: &GErrView<'a, C, D>) -> Self {
136 DisplayJsonData {
137 id: err
138 .id
139 .map(|id| ::serde_json::to_value(id).unwrap_or_default()),
140 code: err.code.map(|s| s.into()),
141 message: err.message.into(),
142 tags: err.tags.map(|t| t.iter().map(|s| s.to_string()).collect()),
143 data: err
144 .data
145 .map(|d| serde_json::to_value(d).unwrap_or_default()),
146 help: err.help.map(Into::into),
147 causes: err
148 .sources
149 .map(|sources| sources.iter().map(|src| src.into()).collect::<Vec<_>>()),
150 }
151 }
152}
153
154impl<C: Config, D> TryFrom<JsonData> for GErr<C, D>
159where
160 C::Id: for<'a> ::serde::Deserialize<'a>,
161 D: for<'a> ::serde::Deserialize<'a>,
162{
163 type Error = GErrDefault;
164
165 fn try_from(value: JsonData) -> Result<Self, Self::Error> {
166 let JsonData {
167 id,
168 code,
169 message,
170 tags,
171 data,
172 help,
173 location,
174 sources,
175 backtrace: _,
176 } = value;
177
178 let de_id: Option<C::Id> = if let Some(the_id) = id {
179 let id_val: C::Id = serde_json::from_value(the_id).map_err(|err| {
180 gerr!(
181 "failed converting id to ID = {}",
182 core::any::type_name::<C::Id>();
183 source = err,
184 )
185 })?;
186 Some(id_val)
187 } else {
188 C::id()
189 };
190
191 let mut err = GErr::<C, D>::new_with_id_untracked(de_id, message, Location::caller());
192
193 if let Some(data) = data {
194 err = err.set_data(serde_json::from_value(data).map_err(|err| {
195 gerr!(
196 "failed converting data into D = {}",
197 core::any::type_name::<D>();
198 source = err,
199 )
200 })?);
201 }
202
203 if let Some(code) = code {
204 err = err.set_code(code);
205 } else if let Some(const_code) = C::CODE {
206 err = err.set_code(const_code);
207 }
208
209 if let Some(tags) = tags {
210 err = err.add_tags(tags);
211 }
212
213 if let Some(sources) = sources {
214 let gerr_sources: Vec<Source> = sources.into_iter().map(|s| s.into_source()).collect();
215 err = err.set_sources(gerr_sources);
216 }
217
218 if let Some(help) = help {
219 err = err.set_help(help);
220 }
221
222 if let Some(loc) = location {
223 err = err.set_location(ErrorLocation {
224 file: Cow::Owned(loc.file),
225 line: loc.line,
226 column: loc.column,
227 });
228 }
229
230 Ok(err)
231 }
232}
233
234impl From<&Source> for SourceJsonData {
235 fn from(gerr: &Source) -> Self {
236 match gerr {
237 Source::Err(err) => Self {
238 message: err.to_string(),
239 ..Default::default()
240 },
241 Source::GErr(gerr) => Self {
242 id: serde_json::from_value({
243 match &gerr.id_json {
244 Some(::serde_json::Value::Number(num)) => {
245 ::serde_json::Value::from(num.as_i64().unwrap_or_default())
246 }
247 Some(::serde_json::Value::String(s)) => {
248 ::serde_json::Value::from(s.as_str())
249 }
250 Some(::serde_json::Value::Bool(b)) => ::serde_json::Value::from(*b),
251 Some(::serde_json::Value::Array(arr)) => {
252 ::serde_json::Value::from(arr.as_slice())
253 }
254 Some(::serde_json::Value::Object(obj)) => {
255 ::serde_json::Value::from(obj.clone())
256 }
257 _ => ::serde_json::Value::Null,
258 }
259 })
260 .unwrap_or_default(),
261 code: gerr.code.as_deref().map(|s| s.into()),
262 message: gerr.message.to_string(),
263 tags: gerr
264 .tags
265 .as_ref()
266 .map(|t| t.iter().map(|t| t.to_string()).collect()),
267 data: serde_json::from_value({
268 if let Some(json) = &gerr.data_json {
269 match json {
270 ::serde_json::Value::Bool(b) => ::serde_json::Value::from(*b),
271 ::serde_json::Value::Number(num) => {
272 ::serde_json::Value::from(num.as_i64().unwrap_or_default())
273 }
274 ::serde_json::Value::String(s) => ::serde_json::Value::from(s.as_str()),
275 ::serde_json::Value::Array(arr) => {
276 let slice: &[::serde_json::Value] = arr.as_ref();
277 ::serde_json::Value::from(slice)
278 }
279 serde_json::Value::Object(obj) => {
280 ::serde_json::Value::Object(obj.clone())
281 }
282 _ => ::serde_json::Value::Null,
283 }
284 } else {
285 ::serde_json::Value::Null
286 }
287 })
288 .unwrap_or_default(),
289 help: gerr.help.as_deref().map(|s| s.into()),
290 sources: gerr
291 .sources
292 .as_ref()
293 .map(|s| s.iter().map(|s| s.into()).collect()),
294 location: gerr.location.as_ref().map(|loc| LocationJsonData {
295 file: loc.file.to_string(),
296 line: loc.line,
297 column: loc.column,
298 }),
299 },
300 }
301 }
302}
303
304impl From<&Source> for DisplayCausesJsonData {
305 fn from(value: &Source) -> Self {
306 match value {
307 Source::Err(err) => Self {
308 message: err.to_string(),
309 caused_by: None,
310 },
311 Source::GErr(gerr) => Self {
312 message: gerr.to_string(),
313 caused_by: gerr
314 .sources
315 .as_deref()
316 .map(|s| s.iter().map(|i| i.into()).collect::<Vec<_>>()),
317 },
318 }
319 }
320}
321
322impl SourceJsonData {
323 fn into_source(self) -> Source {
324 let SourceJsonData {
325 id,
326 code,
327 message,
328 tags,
329 data,
330 help,
331 location,
332 sources,
333 } = self;
334
335 let gerr_source = GErrSource {
336 id: match id {
337 Some(serde_json::Value::Bool(b)) => Some(Box::new(b)),
338 Some(serde_json::Value::Number(ref num)) => {
339 Some(Box::new(num.as_i64().unwrap_or_default()))
340 }
341 _ => id
342 .as_ref()
343 .map(|id| Box::new(id.to_string()) as Box<dyn IdSource>),
344 },
345
346 id_json: id,
347
348 message: message.into(),
349
350 code: code.map(Cow::Owned),
351
352 sources: sources.map(|s| s.into_iter().map(|sj| sj.into_source()).collect()),
353
354 tags: tags.map(|tags| tags.into_iter().map(Cow::Owned).collect()),
355
356 data: data
357 .as_ref()
358 .map(|v| Box::new(v.to_string()) as Box<dyn DataSource>),
359
360 data_json: data,
361
362 help: help.map(Cow::Owned),
363
364 location: location.map(|loc| ErrorLocation {
365 file: Cow::Owned(loc.file),
366 line: loc.line,
367 column: loc.column,
368 }),
369 };
370
371 Source::GErr(Box::new(gerr_source))
372 }
373}