ferrin_spec/error/
data.rs1use super::BoxError;
4use crate::json::JsonValue;
5
6#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
8#[error("{message}")]
9pub struct EmptyResponseBodyError {
10 pub message: String,
12}
13
14impl EmptyResponseBodyError {
15 #[must_use]
17 pub fn new() -> Self {
18 Self {
19 message: "empty response body".to_owned(),
20 }
21 }
22
23 #[must_use]
25 pub fn with_message(message: impl Into<String>) -> Self {
26 Self {
27 message: message.into(),
28 }
29 }
30}
31
32impl Default for EmptyResponseBodyError {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
40#[error("{message}")]
41pub struct NoContentGeneratedError {
42 pub message: String,
44}
45
46impl NoContentGeneratedError {
47 #[must_use]
49 pub fn new() -> Self {
50 Self {
51 message: "no content generated".to_owned(),
52 }
53 }
54
55 #[must_use]
57 pub fn with_message(message: impl Into<String>) -> Self {
58 Self {
59 message: message.into(),
60 }
61 }
62}
63
64impl Default for NoContentGeneratedError {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70#[derive(Debug, thiserror::Error)]
72#[error("{message}")]
73pub struct InvalidResponseDataError {
74 pub message: String,
76 pub data: JsonValue,
78}
79
80impl InvalidResponseDataError {
81 #[must_use]
83 pub fn new(message: impl Into<String>, data: JsonValue) -> Self {
84 Self {
85 message: message.into(),
86 data,
87 }
88 }
89
90 #[must_use]
92 pub fn from_data(data: JsonValue) -> Self {
93 let rendered = data.to_string();
94 let message = format!(
95 "invalid response data: {}",
96 super::truncate_for_display(&rendered, 512)
97 );
98 Self { message, data }
99 }
100}
101
102#[derive(Debug, thiserror::Error)]
104#[error("json parsing failed: {cause}")]
105pub struct JsonParseError {
106 pub text: String,
108 #[source]
110 pub cause: BoxError,
111}
112
113impl JsonParseError {
114 #[must_use]
116 pub fn new(
117 text: impl Into<String>,
118 cause: impl std::error::Error + Send + Sync + 'static,
119 ) -> Self {
120 Self {
121 text: text.into(),
122 cause: Box::new(cause),
123 }
124 }
125}
126
127#[derive(Debug, Clone, Default, PartialEq, Eq)]
129pub struct TypeValidationContext {
130 pub field: Option<String>,
132 pub entity_name: Option<String>,
134 pub entity_id: Option<String>,
136}
137
138#[derive(Debug, thiserror::Error)]
140pub struct TypeValidationError {
141 pub value: JsonValue,
143 pub context: Option<Box<TypeValidationContext>>,
145 #[source]
147 pub cause: BoxError,
148}
149
150impl TypeValidationError {
151 #[must_use]
153 pub fn new(value: JsonValue, cause: impl std::error::Error + Send + Sync + 'static) -> Self {
154 Self {
155 value,
156 context: None,
157 cause: Box::new(cause),
158 }
159 }
160
161 #[must_use]
163 pub fn with_context(mut self, context: TypeValidationContext) -> Self {
164 self.context = Some(Box::new(context));
165 self
166 }
167
168 #[must_use]
171 pub fn wrap(value: JsonValue, cause: BoxError, context: Option<TypeValidationContext>) -> Self {
172 let context = context.map(Box::new);
173 match cause.downcast::<TypeValidationError>() {
174 Ok(existing) if existing.value == value && existing.context == context => *existing,
175 Ok(existing) => Self {
176 value,
177 context,
178 cause: existing,
179 },
180 Err(cause) => Self {
181 value,
182 context,
183 cause,
184 },
185 }
186 }
187}
188
189impl std::fmt::Display for TypeValidationError {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 f.write_str("type validation failed")?;
192 if let Some(context) = &self.context {
193 if let Some(field) = &context.field {
194 write!(f, " for {field}")?;
195 }
196 let mut parts = Vec::new();
197 if let Some(name) = &context.entity_name {
198 parts.push(name.clone());
199 }
200 if let Some(id) = &context.entity_id {
201 parts.push(format!("id: \"{id}\""));
202 }
203 if !parts.is_empty() {
204 write!(f, " ({})", parts.join(", "))?;
205 }
206 }
207 write!(f, ": {}", self.cause)
208 }
209}