1use crate::gerr::Source;
2use crate::report::json_data::{DisplayJsonData, JsonData};
3use crate::{Config, gerr_view::GErrView, report::Report};
4use alloc::borrow::Cow;
5use core::fmt::{Debug, Display};
6
7pub struct JsonReport;
9
10impl JsonReport {
11 pub fn data<E, C: Config, D>(err: &E) -> JsonData
13 where
14 for<'a> &'a E: Into<GErrView<'a, C, D>>,
15 C::Id: serde::Serialize,
16 D: serde::Serialize,
17 {
18 let err = &err.into();
19 err.into()
20 }
21
22 pub fn display_data<E, C: Config, D>(err: &E) -> DisplayJsonData
24 where
25 for<'a> &'a E: Into<GErrView<'a, C, D>>,
26 C::Id: serde::Serialize,
27 D: serde::Serialize,
28 {
29 let err = &err.into();
30 err.into()
31 }
32}
33
34impl Report for JsonReport {
35 fn report<E, C: Config, D>(err: &E) -> String
36 where
37 for<'a> &'a E: Into<GErrView<'a, C, D>>,
38 C::Id: Display + serde::Serialize,
39 D: Debug + serde::Serialize,
40 {
41 let err = &err.into();
42 let resp: JsonReportData = err.into();
43 serde_json::to_string_pretty(&resp).unwrap_or("<invalid json format>".into())
44 }
45}
46
47#[derive(serde::Serialize)]
48struct JsonReportData<'a> {
49 pub id: Option<serde_json::Value>,
50 pub code: Option<&'a str>,
51 pub message: &'a str,
52 pub tags: Option<&'a [Cow<'static, str>]>,
53 pub data: Option<serde_json::Value>,
54 pub location: LocationJson<'a>,
55 pub sources: Option<Vec<SourceJson<'a>>>,
56 pub help: Option<&'a str>,
57
58 #[cfg(feature = "backtrace")]
59 pub backtrace: String,
60}
61
62#[derive(serde::Serialize)]
63struct LocationJson<'a> {
64 pub file: &'a str,
65 pub line: u32,
66 pub column: u32,
67}
68
69#[derive(serde::Serialize)]
70struct SourceJson<'a> {
71 pub id: Option<&'a serde_json::Value>,
72 pub code: Option<&'a str>,
73 pub message: Cow<'a, str>,
74 pub tags: Option<&'a [Cow<'static, str>]>,
75 pub data: Option<&'a serde_json::Value>,
76 pub location: Option<LocationJson<'a>>,
77 pub sources: Option<Vec<SourceJson<'a>>>,
78 pub help: Option<&'a str>,
79}
80
81impl<'a, C: Config, D> From<&'a GErrView<'a, C, D>> for JsonReportData<'a>
82where
83 C::Id: serde::Serialize,
84 D: serde::Serialize,
85{
86 fn from(value: &'a GErrView<C, D>) -> Self {
87 Self {
88 id: value
89 .id
90 .map(|id| ::serde_json::to_value(id).unwrap_or_default()),
91 code: value.code,
92 message: value.message,
93 tags: value.tags,
94 data: serde_json::to_value(value.data).ok(),
95 location: LocationJson {
96 file: &value.location.file,
97 line: value.location.line,
98 column: value.location.column,
99 },
100 sources: value.sources.map(|s| s.iter().map(|v| v.into()).collect()),
101 help: value.help,
102
103 #[cfg(feature = "backtrace")]
104 backtrace: format!("{:#?}", value.backtrace),
105 }
106 }
107}
108
109impl<'a> From<&'a Source> for SourceJson<'a> {
110 fn from(source: &'a Source) -> Self {
111 match source {
112 Source::Err(err) => Self {
113 id: None,
114 code: None,
115 message: Cow::Owned(err.to_string()),
116 tags: None,
117 data: None,
118 location: None,
119 sources: None,
120 help: None,
121 },
122 Source::GErr(gerr) => Self {
123 id: gerr.id_json.as_ref(),
124 code: gerr.code.as_deref(),
125 message: Cow::Borrowed(&gerr.message),
126 tags: gerr.tags.as_deref(),
127 data: gerr.data_json.as_ref(),
128 location: gerr.location.as_ref().map(|loc| LocationJson {
129 file: &loc.file,
130 line: loc.line,
131 column: loc.column,
132 }),
133 sources: gerr
134 .sources
135 .as_deref()
136 .map(|s| s.iter().map(|src| src.into()).collect()),
137 help: gerr.help.as_deref(),
138 },
139 }
140 }
141}