gproxy_protocol/transform/
error.rs1use std::{error::Error, fmt};
2
3#[derive(Debug)]
6pub struct UpstreamError<T>(pub T);
7
8impl<T: serde::Serialize> fmt::Display for UpstreamError<T> {
9 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
10 let json = serde_json::to_string(&self.0).map_err(|_| fmt::Error)?;
11 f.write_str(&json)
12 }
13}
14
15impl<T: fmt::Debug + serde::Serialize> Error for UpstreamError<T> {}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
20pub enum TransformErrorKind {
21 InvalidInput,
22 InvalidResult,
23 Unsupported,
24 MissingMetadata,
25 MissingState,
26 Conflict,
27 Limit,
28 Host,
29}
30
31#[derive(Debug)]
33pub struct TransformError {
34 kind: TransformErrorKind,
35 context: String,
36 detail: String,
37 source: Option<Box<dyn Error + Send + Sync + 'static>>,
38}
39
40impl TransformError {
41 pub fn new(
42 kind: TransformErrorKind,
43 context: impl Into<String>,
44 detail: impl Into<String>,
45 ) -> Self {
46 Self {
47 kind,
48 context: context.into(),
49 detail: detail.into(),
50 source: None,
51 }
52 }
53
54 pub fn with_source(
55 kind: TransformErrorKind,
56 context: impl Into<String>,
57 detail: impl Into<String>,
58 source: impl Into<Box<dyn Error + Send + Sync + 'static>>,
59 ) -> Self {
60 Self {
61 kind,
62 context: context.into(),
63 detail: detail.into(),
64 source: Some(source.into()),
65 }
66 }
67
68 pub fn shape(context: impl Into<String>, detail: impl Into<String>) -> Self {
69 Self::new(TransformErrorKind::InvalidInput, context, detail)
70 }
71
72 pub fn unsupported(context: impl Into<String>, detail: impl Into<String>) -> Self {
73 Self::new(TransformErrorKind::Unsupported, context, detail)
74 }
75
76 pub fn invalid_result(context: impl Into<String>, detail: impl Into<String>) -> Self {
77 Self::new(TransformErrorKind::InvalidResult, context, detail)
78 }
79
80 pub fn upstream<T>(context: impl Into<String>, payload: T) -> Self
82 where
83 T: fmt::Debug + serde::Serialize + Send + Sync + 'static,
84 {
85 let error = UpstreamError(payload);
86 Self::with_source(
87 TransformErrorKind::InvalidResult,
88 context,
89 error.to_string(),
90 error,
91 )
92 }
93
94 pub fn missing_metadata(field: impl Into<String>) -> Self {
95 Self::new(
96 TransformErrorKind::MissingMetadata,
97 field,
98 "required factual metadata is unavailable",
99 )
100 }
101
102 pub fn kind(&self) -> TransformErrorKind {
103 self.kind
104 }
105 pub fn context(&self) -> &str {
106 &self.context
107 }
108 pub fn detail(&self) -> &str {
109 &self.detail
110 }
111}
112
113impl fmt::Display for TransformError {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 write!(f, "{}: {}", self.context, self.detail)
116 }
117}
118
119impl Error for TransformError {
120 fn source(&self) -> Option<&(dyn Error + 'static)> {
121 self.source
122 .as_deref()
123 .map(|source| source as &(dyn Error + 'static))
124 }
125}
126
127impl From<serde_json::Error> for TransformError {
128 fn from(error: serde_json::Error) -> Self {
129 let message = error.to_string();
130 Self::with_source(TransformErrorKind::InvalidInput, "JSON", message, error)
131 }
132}
133
134impl From<crate::capability::CapabilityError> for TransformError {
135 fn from(error: crate::capability::CapabilityError) -> Self {
136 use crate::capability::CapabilityErrorKind;
137 let kind = match error.kind() {
138 CapabilityErrorKind::Unsupported => TransformErrorKind::Unsupported,
139 CapabilityErrorKind::NotFound | CapabilityErrorKind::Expired => {
140 TransformErrorKind::Host
141 }
142 CapabilityErrorKind::Conflict => TransformErrorKind::Conflict,
143 CapabilityErrorKind::Limit => TransformErrorKind::Limit,
144 CapabilityErrorKind::Invalid => TransformErrorKind::InvalidInput,
145 CapabilityErrorKind::Transport | CapabilityErrorKind::Storage => {
146 TransformErrorKind::Host
147 }
148 };
149 let message = error.to_string();
150 Self::with_source(kind, "host capability", message, error)
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
157pub enum DiagnosticKind {
158 OmittedOptional,
161 RepresentationChanged,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct Diagnostic {
166 pub field: String,
167 pub kind: DiagnosticKind,
168 pub message: String,
169}
170
171#[derive(Debug, Default, Clone, PartialEq, Eq)]
173pub struct Report {
174 pub diagnostics: Vec<Diagnostic>,
175}
176
177impl Report {
178 pub fn omitted(&mut self, field: impl Into<String>, message: impl Into<String>) {
179 self.diagnostics.push(Diagnostic {
180 field: field.into(),
181 kind: DiagnosticKind::OmittedOptional,
182 message: message.into(),
183 });
184 }
185
186 pub fn changed(&mut self, field: impl Into<String>, message: impl Into<String>) {
187 self.diagnostics.push(Diagnostic {
188 field: field.into(),
189 kind: DiagnosticKind::RepresentationChanged,
190 message: message.into(),
191 });
192 }
193}