dynamic_graphql/
errors.rs

1use std::fmt::Display;
2use std::marker::PhantomData;
3
4use async_graphql::ErrorExtensionValues;
5use async_graphql::Pos;
6use async_graphql::ServerError;
7use async_graphql::dynamic::TypeRef;
8
9use crate::Value;
10use crate::types::GetInputTypeRef;
11
12fn get_type_name<T: GetInputTypeRef>() -> String {
13    let type_ref: TypeRef = <Option<T>>::get_input_type_ref().into();
14    type_ref.to_string()
15}
16
17#[derive(Debug)]
18pub struct InputValueError<T> {
19    message: String,
20    extensions: Option<ErrorExtensionValues>,
21    phantom: PhantomData<T>,
22}
23
24impl<T> InputValueError<T> {
25    pub fn new(message: String) -> Self {
26        Self {
27            message,
28            extensions: None,
29            phantom: PhantomData,
30        }
31    }
32    /// The expected input type did not match the actual input type.
33    #[must_use]
34    pub fn expected_type(actual: Value) -> Self
35    where
36        T: GetInputTypeRef,
37    {
38        Self::new(format!(
39            r#"Expected input type "{}", found {}."#,
40            get_type_name::<T>(),
41            actual
42        ))
43    }
44    /// A custom error message.
45    ///
46    /// Any type that implements `Display` is automatically converted to this if
47    /// you use the `?` operator.
48    #[must_use]
49    pub fn custom(msg: impl Display) -> Self
50    where
51        T: GetInputTypeRef,
52    {
53        Self::new(format!(
54            r#"Failed to parse "{}": {}"#,
55            get_type_name::<T>(),
56            msg
57        ))
58    }
59
60    /// Propagate the error message to a different type.
61    pub fn propagate<U: GetInputTypeRef>(self) -> InputValueError<U>
62    where
63        T: GetInputTypeRef,
64    {
65        if get_type_name::<T>() != get_type_name::<U>() {
66            InputValueError::new(format!(
67                r#"{} (occurred while parsing "{}")"#,
68                self.message,
69                get_type_name::<U>()
70            ))
71        } else {
72            InputValueError::new(self.message)
73        }
74    }
75    pub fn with_extension(mut self, name: impl AsRef<str>, value: impl Into<Value>) -> Self {
76        self.extensions
77            .get_or_insert_with(ErrorExtensionValues::default)
78            .set(name, value);
79        self
80    }
81
82    /// Convert the error into a server error.
83    pub fn into_server_error(self, pos: Pos) -> ServerError {
84        let mut err = ServerError::new(self.message, Some(pos));
85        err.extensions = self.extensions;
86        err
87    }
88
89    pub fn into_arg_error(self, name: &str) -> crate::Error {
90        let mut error = crate::Error::new(format!(
91            "Invalid value for argument \"{}\": {}",
92            name, self.message
93        ));
94        error.extensions = self.extensions;
95        error
96    }
97    pub fn into_field_error(self, name: &str) -> crate::Error {
98        let mut error = crate::Error::new(format!(
99            "Invalid value for field \"{}\": {}",
100            name, self.message
101        ));
102        error.extensions = self.extensions;
103        error
104    }
105}
106
107impl<T: GetInputTypeRef> From<async_graphql::Error> for InputValueError<T> {
108    fn from(value: async_graphql::Error) -> Self {
109        let mut err = Self::custom(value.message);
110        err.extensions = value.extensions;
111        err
112    }
113}
114
115impl<T> From<InputValueError<T>> for async_graphql::Error {
116    fn from(value: InputValueError<T>) -> Self {
117        Self {
118            message: value.message,
119            source: None,
120            extensions: value.extensions,
121        }
122    }
123}
124
125pub type InputValueResult<T> = Result<T, InputValueError<T>>;