async_graphql/
error.rs

1use std::{
2    any::Any,
3    collections::BTreeMap,
4    fmt::{self, Debug, Display, Formatter},
5    marker::PhantomData,
6    sync::Arc,
7};
8
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use crate::{parser, InputType, Pos, Value};
13
14/// Extensions to the error.
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
16#[serde(transparent)]
17pub struct ErrorExtensionValues(BTreeMap<String, Value>);
18
19impl ErrorExtensionValues {
20    /// Set an extension value.
21    pub fn set(&mut self, name: impl AsRef<str>, value: impl Into<Value>) {
22        self.0.insert(name.as_ref().to_string(), value.into());
23    }
24
25    /// Unset an extension value.
26    pub fn unset(&mut self, name: impl AsRef<str>) {
27        self.0.remove(name.as_ref());
28    }
29
30    /// Get an extension value.
31    pub fn get(&self, name: impl AsRef<str>) -> Option<&Value> {
32        self.0.get(name.as_ref())
33    }
34}
35
36/// An error in a GraphQL server.
37#[derive(Clone, Serialize, Deserialize)]
38pub struct ServerError {
39    /// An explanatory message of the error.
40    pub message: String,
41    /// The source of the error.
42    #[serde(skip)]
43    pub source: Option<Arc<dyn Any + Send + Sync>>,
44    /// Where the error occurred.
45    #[serde(skip_serializing_if = "Vec::is_empty", default)]
46    pub locations: Vec<Pos>,
47    /// If the error occurred in a resolver, the path to the error.
48    #[serde(skip_serializing_if = "Vec::is_empty", default)]
49    pub path: Vec<PathSegment>,
50    /// Extensions to the error.
51    #[serde(skip_serializing_if = "error_extensions_is_empty", default)]
52    pub extensions: Option<ErrorExtensionValues>,
53}
54
55fn error_extensions_is_empty(values: &Option<ErrorExtensionValues>) -> bool {
56    values.as_ref().is_none_or(|values| values.0.is_empty())
57}
58
59impl Debug for ServerError {
60    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
61        f.debug_struct("ServerError")
62            .field("message", &self.message)
63            .field("locations", &self.locations)
64            .field("path", &self.path)
65            .field("extensions", &self.extensions)
66            .finish()
67    }
68}
69
70impl PartialEq for ServerError {
71    fn eq(&self, other: &Self) -> bool {
72        self.message.eq(&other.message)
73            && self.locations.eq(&other.locations)
74            && self.path.eq(&other.path)
75            && self.extensions.eq(&other.extensions)
76    }
77}
78
79impl ServerError {
80    /// Create a new server error with the message.
81    pub fn new(message: impl Into<String>, pos: Option<Pos>) -> Self {
82        Self {
83            message: message.into(),
84            source: None,
85            locations: pos.map(|pos| vec![pos]).unwrap_or_default(),
86            path: Vec::new(),
87            extensions: None,
88        }
89    }
90
91    /// Get the source of the error.
92    ///
93    /// # Examples
94    ///
95    /// ```rust
96    /// use std::io::ErrorKind;
97    ///
98    /// use async_graphql::*;
99    ///
100    /// struct Query;
101    ///
102    /// #[Object]
103    /// impl Query {
104    ///     async fn value(&self) -> Result<i32> {
105    ///         Err(Error::new_with_source(std::io::Error::new(
106    ///             ErrorKind::Other,
107    ///             "my error",
108    ///         )))
109    ///     }
110    /// }
111    ///
112    /// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
113    ///
114    /// # tokio::runtime::Runtime::new().unwrap().block_on(async move {
115    /// let err = schema
116    ///     .execute("{ value }")
117    ///     .await
118    ///     .into_result()
119    ///     .unwrap_err()
120    ///     .remove(0);
121    /// assert!(err.source::<std::io::Error>().is_some());
122    /// # });
123    /// ```
124    pub fn source<T: Any + Send + Sync>(&self) -> Option<&T> {
125        self.source.as_ref().map(|err| err.downcast_ref()).flatten()
126    }
127
128    #[doc(hidden)]
129    #[must_use]
130    pub fn with_path(self, path: Vec<PathSegment>) -> Self {
131        Self { path, ..self }
132    }
133}
134
135impl Display for ServerError {
136    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
137        f.write_str(&self.message)
138    }
139}
140
141impl From<ServerError> for Vec<ServerError> {
142    fn from(single: ServerError) -> Self {
143        vec![single]
144    }
145}
146
147impl From<parser::Error> for ServerError {
148    fn from(e: parser::Error) -> Self {
149        Self {
150            message: e.to_string(),
151            source: None,
152            locations: e.positions().collect(),
153            path: Vec::new(),
154            extensions: None,
155        }
156    }
157}
158
159/// A segment of path to a resolver.
160///
161/// This is like [`QueryPathSegment`](enum.QueryPathSegment.html), but owned and
162/// used as a part of errors instead of during execution.
163#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
164#[serde(untagged)]
165pub enum PathSegment {
166    /// A field in an object.
167    Field(String),
168    /// An index in a list.
169    Index(usize),
170}
171
172/// Alias for `Result<T, ServerError>`.
173pub type ServerResult<T> = std::result::Result<T, ServerError>;
174
175/// An error parsing an input value.
176///
177/// This type is generic over T as it uses T's type name when converting to a
178/// regular error.
179#[derive(Debug)]
180pub struct InputValueError<T> {
181    message: String,
182    extensions: Option<ErrorExtensionValues>,
183    phantom: PhantomData<T>,
184}
185
186impl<T: InputType> InputValueError<T> {
187    fn new(message: String, extensions: Option<ErrorExtensionValues>) -> Self {
188        Self {
189            message,
190            extensions,
191            phantom: PhantomData,
192        }
193    }
194
195    /// The expected input type did not match the actual input type.
196    #[must_use]
197    pub fn expected_type(actual: Value) -> Self {
198        Self::new(
199            format!(
200                r#"Expected input type "{}", found {}."#,
201                T::type_name(),
202                actual
203            ),
204            None,
205        )
206    }
207
208    /// A custom error message.
209    ///
210    /// Any type that implements `Display` is automatically converted to this if
211    /// you use the `?` operator.
212    #[must_use]
213    pub fn custom(msg: impl Display) -> Self {
214        Self::new(
215            format!(r#"Failed to parse "{}": {}"#, T::type_name(), msg),
216            None,
217        )
218    }
219
220    /// Propagate the error message to a different type.
221    pub fn propagate<U: InputType>(self) -> InputValueError<U> {
222        if T::type_name() != U::type_name() {
223            InputValueError::new(
224                format!(
225                    r#"{} (occurred while parsing "{}")"#,
226                    self.message,
227                    U::type_name()
228                ),
229                self.extensions,
230            )
231        } else {
232            InputValueError::new(self.message, self.extensions)
233        }
234    }
235
236    /// Set an extension value.
237    pub fn with_extension(mut self, name: impl AsRef<str>, value: impl Into<Value>) -> Self {
238        self.extensions
239            .get_or_insert_with(ErrorExtensionValues::default)
240            .set(name, value);
241        self
242    }
243
244    /// Convert the error into a server error.
245    pub fn into_server_error(self, pos: Pos) -> ServerError {
246        let mut err = ServerError::new(self.message, Some(pos));
247        err.extensions = self.extensions;
248        err
249    }
250}
251
252impl<T: InputType, E: Display> From<E> for InputValueError<T> {
253    fn from(error: E) -> Self {
254        Self::custom(error)
255    }
256}
257
258/// An error parsing a value of type `T`.
259pub type InputValueResult<T> = Result<T, InputValueError<T>>;
260
261/// An error with a message and optional extensions.
262#[derive(Clone, Serialize)]
263pub struct Error {
264    /// The error message.
265    pub message: String,
266    /// The source of the error.
267    #[serde(skip)]
268    pub source: Option<Arc<dyn Any + Send + Sync>>,
269    /// Extensions to the error.
270    #[serde(skip_serializing_if = "error_extensions_is_empty")]
271    pub extensions: Option<ErrorExtensionValues>,
272}
273
274impl Debug for Error {
275    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
276        f.debug_struct("Error")
277            .field("message", &self.message)
278            .field("extensions", &self.extensions)
279            .finish()
280    }
281}
282
283impl PartialEq for Error {
284    fn eq(&self, other: &Self) -> bool {
285        self.message.eq(&other.message) && self.extensions.eq(&other.extensions)
286    }
287}
288
289impl Error {
290    /// Create an error from the given error message.
291    pub fn new(message: impl Into<String>) -> Self {
292        Self {
293            message: message.into(),
294            source: None,
295            extensions: None,
296        }
297    }
298
299    /// Create an error with a type that implements `Display`, and it will also
300    /// set the `source` of the error to this value.
301    pub fn new_with_source(source: impl Display + Send + Sync + 'static) -> Self {
302        Self {
303            message: source.to_string(),
304            source: Some(Arc::new(source)),
305            extensions: None,
306        }
307    }
308
309    /// Convert the error to a server error.
310    #[must_use]
311    pub fn into_server_error(self, pos: Pos) -> ServerError {
312        ServerError {
313            message: self.message,
314            source: self.source,
315            locations: vec![pos],
316            path: Vec::new(),
317            extensions: self.extensions,
318        }
319    }
320}
321
322#[cfg(not(feature = "custom-error-conversion"))]
323impl<T: Display + Send + Sync + 'static> From<T> for Error {
324    fn from(e: T) -> Self {
325        Self {
326            message: e.to_string(),
327            source: Some(Arc::new(e)),
328            extensions: None,
329        }
330    }
331}
332
333#[cfg(feature = "custom-error-conversion")]
334impl From<&'static str> for Error {
335    fn from(e: &'static str) -> Self {
336        Self {
337            message: e.to_string(),
338            source: None,
339            extensions: None,
340        }
341    }
342}
343
344#[cfg(feature = "custom-error-conversion")]
345impl From<String> for Error {
346    fn from(e: String) -> Self {
347        Self {
348            message: e,
349            source: None,
350            extensions: None,
351        }
352    }
353}
354
355/// An alias for `Result<T, Error>`.
356pub type Result<T, E = Error> = std::result::Result<T, E>;
357
358/// An error parsing the request.
359#[derive(Debug, Error)]
360#[non_exhaustive]
361pub enum ParseRequestError {
362    /// An IO error occurred.
363    #[error("{0}")]
364    Io(#[from] std::io::Error),
365
366    /// The request's syntax was invalid.
367    #[error("Invalid request: {0}")]
368    InvalidRequest(Box<dyn std::error::Error + Send + Sync>),
369
370    /// The request's files map was invalid.
371    #[error("Invalid files map: {0}")]
372    InvalidFilesMap(Box<dyn std::error::Error + Send + Sync>),
373
374    /// The request's multipart data was invalid.
375    #[error("Invalid multipart data")]
376    InvalidMultipart(multer::Error),
377
378    /// Missing "operators" part for multipart request.
379    #[error("Missing \"operators\" part")]
380    MissingOperatorsPart,
381
382    /// Missing "map" part for multipart request.
383    #[error("Missing \"map\" part")]
384    MissingMapPart,
385
386    /// It's not an upload operation
387    #[error("It's not an upload operation")]
388    NotUpload,
389
390    /// Files were missing the request.
391    #[error("Missing files")]
392    MissingFiles,
393
394    /// The request's payload is too large, and this server rejected it.
395    #[error("Payload too large")]
396    PayloadTooLarge,
397
398    /// The request is a batch request, but the server does not support batch
399    /// requests.
400    #[error("Batch requests are not supported")]
401    UnsupportedBatch,
402}
403
404impl From<multer::Error> for ParseRequestError {
405    fn from(err: multer::Error) -> Self {
406        match err {
407            multer::Error::FieldSizeExceeded { .. } | multer::Error::StreamSizeExceeded { .. } => {
408                ParseRequestError::PayloadTooLarge
409            }
410            _ => ParseRequestError::InvalidMultipart(err),
411        }
412    }
413}
414
415impl From<mime::FromStrError> for ParseRequestError {
416    fn from(e: mime::FromStrError) -> Self {
417        Self::InvalidRequest(Box::new(e))
418    }
419}
420
421/// An error which can be extended into a `Error`.
422pub trait ErrorExtensions: Sized {
423    /// Convert the error to a `Error`.
424    fn extend(&self) -> Error;
425
426    /// Add extensions to the error, using a callback to make the extensions.
427    fn extend_with<C>(self, cb: C) -> Error
428    where
429        C: FnOnce(&Self, &mut ErrorExtensionValues),
430    {
431        let mut new_extensions = Default::default();
432        cb(&self, &mut new_extensions);
433
434        let Error {
435            message,
436            source,
437            extensions,
438        } = self.extend();
439
440        let mut extensions = extensions.unwrap_or_default();
441        extensions.0.extend(new_extensions.0);
442
443        Error {
444            message,
445            source,
446            extensions: Some(extensions),
447        }
448    }
449}
450
451impl ErrorExtensions for Error {
452    fn extend(&self) -> Error {
453        self.clone()
454    }
455}
456
457// implementing for &E instead of E gives the user the possibility to implement
458// for E which does not conflict with this implementation acting as a fallback.
459impl<E: Display> ErrorExtensions for &E {
460    fn extend(&self) -> Error {
461        Error {
462            message: self.to_string(),
463            source: None,
464            extensions: None,
465        }
466    }
467}
468
469/// Extend a `Result`'s error value with
470/// [`ErrorExtensions`](trait.ErrorExtensions.html).
471pub trait ResultExt<T, E>: Sized {
472    /// Extend the error value of the result with the callback.
473    fn extend_err<C>(self, cb: C) -> Result<T>
474    where
475        C: FnOnce(&E, &mut ErrorExtensionValues);
476
477    /// Extend the result to a `Result`.
478    fn extend(self) -> Result<T>;
479}
480
481// This is implemented on E and not &E which means it cannot be used on foreign
482// types. (see example).
483impl<T, E> ResultExt<T, E> for std::result::Result<T, E>
484where
485    E: ErrorExtensions + Send + Sync + 'static,
486{
487    fn extend_err<C>(self, cb: C) -> Result<T>
488    where
489        C: FnOnce(&E, &mut ErrorExtensionValues),
490    {
491        match self {
492            Err(err) => Err(err.extend_with(|e, ee| cb(e, ee))),
493            Ok(value) => Ok(value),
494        }
495    }
496
497    fn extend(self) -> Result<T> {
498        match self {
499            Err(err) => Err(err.extend()),
500            Ok(value) => Ok(value),
501        }
502    }
503}