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 missing_metadata(field: impl Into<String>) -> Self {
81 Self::new(
82 TransformErrorKind::MissingMetadata,
83 field,
84 "required factual metadata is unavailable",
85 )
86 }
87
88 pub fn kind(&self) -> TransformErrorKind {
89 self.kind
90 }
91 pub fn context(&self) -> &str {
92 &self.context
93 }
94 pub fn detail(&self) -> &str {
95 &self.detail
96 }
97}
98
99impl fmt::Display for TransformError {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "{}: {}", self.context, self.detail)
102 }
103}
104
105impl Error for TransformError {
106 fn source(&self) -> Option<&(dyn Error + 'static)> {
107 self.source
108 .as_deref()
109 .map(|source| source as &(dyn Error + 'static))
110 }
111}
112
113impl From<serde_json::Error> for TransformError {
114 fn from(error: serde_json::Error) -> Self {
115 let message = error.to_string();
116 Self::with_source(TransformErrorKind::InvalidInput, "JSON", message, error)
117 }
118}
119
120impl From<crate::capability::CapabilityError> for TransformError {
121 fn from(error: crate::capability::CapabilityError) -> Self {
122 use crate::capability::CapabilityErrorKind;
123 let kind = match error.kind() {
124 CapabilityErrorKind::Unsupported => TransformErrorKind::Unsupported,
125 CapabilityErrorKind::NotFound | CapabilityErrorKind::Expired => {
126 TransformErrorKind::Host
127 }
128 CapabilityErrorKind::Conflict => TransformErrorKind::Conflict,
129 CapabilityErrorKind::Limit => TransformErrorKind::Limit,
130 CapabilityErrorKind::Invalid => TransformErrorKind::InvalidInput,
131 CapabilityErrorKind::Transport | CapabilityErrorKind::Storage => {
132 TransformErrorKind::Host
133 }
134 };
135 let message = error.to_string();
136 Self::with_source(kind, "host capability", message, error)
137 }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
143pub enum DiagnosticKind {
144 OmittedOptional,
147 RepresentationChanged,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub struct Diagnostic {
152 pub field: String,
153 pub kind: DiagnosticKind,
154 pub message: String,
155}
156
157#[derive(Debug, Default, Clone, PartialEq, Eq)]
159pub struct Report {
160 pub diagnostics: Vec<Diagnostic>,
161}
162
163impl Report {
164 pub fn omitted(&mut self, field: impl Into<String>, message: impl Into<String>) {
165 self.diagnostics.push(Diagnostic {
166 field: field.into(),
167 kind: DiagnosticKind::OmittedOptional,
168 message: message.into(),
169 });
170 }
171
172 pub fn changed(&mut self, field: impl Into<String>, message: impl Into<String>) {
173 self.diagnostics.push(Diagnostic {
174 field: field.into(),
175 kind: DiagnosticKind::RepresentationChanged,
176 message: message.into(),
177 });
178 }
179}