async_graphql/
base.rs

1#[cfg(not(feature = "boxed-trait"))]
2use std::future::Future;
3use std::{
4    borrow::Cow,
5    sync::{Arc, Weak},
6};
7
8use async_graphql_value::ConstValue;
9
10use crate::{
11    parser::types::Field,
12    registry::{self, Registry},
13    ContainerType, Context, ContextSelectionSet, Error, InputValueError, InputValueResult,
14    Positioned, Result, ServerResult, Value,
15};
16
17#[doc(hidden)]
18pub trait Description {
19    fn description() -> &'static str;
20}
21
22/// Used to specify the GraphQL Type name.
23pub trait TypeName: Send + Sync {
24    /// Returns a GraphQL type name.
25    fn type_name() -> Cow<'static, str>;
26}
27
28/// Represents a GraphQL input type.
29pub trait InputType: Send + Sync + Sized {
30    /// The raw type used for validator.
31    ///
32    /// Usually it is `Self`, but the wrapper type is its internal type.
33    ///
34    /// For example:
35    ///
36    /// `i32::RawValueType` is `i32`
37    /// `Option<i32>::RawValueType` is `i32`.
38    type RawValueType: ?Sized;
39
40    /// Type the name.
41    fn type_name() -> Cow<'static, str>;
42
43    /// Qualified typename.
44    fn qualified_type_name() -> String {
45        format!("{}!", Self::type_name())
46    }
47
48    /// Create type information in the registry and return qualified typename.
49    fn create_type_info(registry: &mut registry::Registry) -> String;
50
51    /// Parse from `Value`. None represents undefined.
52    fn parse(value: Option<Value>) -> InputValueResult<Self>;
53
54    /// Convert to a `Value` for introspection.
55    fn to_value(&self) -> Value;
56
57    /// Get the federation fields, only for InputObject.
58    #[doc(hidden)]
59    fn federation_fields() -> Option<String> {
60        None
61    }
62
63    /// Returns a reference to the raw value.
64    fn as_raw_value(&self) -> Option<&Self::RawValueType>;
65}
66
67/// Represents a GraphQL output type.
68#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
69pub trait OutputType: Send + Sync {
70    /// Type the name.
71    fn type_name() -> Cow<'static, str>;
72
73    /// Qualified typename.
74    fn qualified_type_name() -> String {
75        format!("{}!", Self::type_name())
76    }
77
78    /// Introspection type name
79    ///
80    /// Is the return value of field `__typename`, the interface and union
81    /// should return the current type, and the others return `Type::type_name`.
82    fn introspection_type_name(&self) -> Cow<'static, str> {
83        Self::type_name()
84    }
85
86    /// Create type information in the registry and return qualified typename.
87    fn create_type_info(registry: &mut registry::Registry) -> String;
88
89    /// Resolve an output value to `async_graphql::Value`.
90    #[cfg(feature = "boxed-trait")]
91    async fn resolve(
92        &self,
93        ctx: &ContextSelectionSet<'_>,
94        field: &Positioned<Field>,
95    ) -> ServerResult<Value>;
96
97    /// Resolve an output value to `async_graphql::Value`.
98    #[cfg(not(feature = "boxed-trait"))]
99    fn resolve(
100        &self,
101        ctx: &ContextSelectionSet<'_>,
102        field: &Positioned<Field>,
103    ) -> impl Future<Output = ServerResult<Value>> + Send;
104}
105
106#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
107impl<T: OutputType + ?Sized> OutputType for &T {
108    fn type_name() -> Cow<'static, str> {
109        T::type_name()
110    }
111
112    fn create_type_info(registry: &mut Registry) -> String {
113        T::create_type_info(registry)
114    }
115
116    #[allow(clippy::trivially_copy_pass_by_ref)]
117    async fn resolve(
118        &self,
119        ctx: &ContextSelectionSet<'_>,
120        field: &Positioned<Field>,
121    ) -> ServerResult<Value> {
122        T::resolve(*self, ctx, field).await
123    }
124}
125
126#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
127impl<T: OutputType + Sync, E: Into<Error> + Send + Sync + Clone> OutputType for Result<T, E> {
128    fn type_name() -> Cow<'static, str> {
129        T::type_name()
130    }
131
132    fn create_type_info(registry: &mut Registry) -> String {
133        T::create_type_info(registry)
134    }
135
136    async fn resolve(
137        &self,
138        ctx: &ContextSelectionSet<'_>,
139        field: &Positioned<Field>,
140    ) -> ServerResult<Value> {
141        match self {
142            Ok(value) => value.resolve(ctx, field).await,
143            Err(err) => Err(ctx.set_error_path(err.clone().into().into_server_error(field.pos))),
144        }
145    }
146}
147
148/// A GraphQL object.
149pub trait ObjectType: ContainerType {}
150
151impl<T: ObjectType + ?Sized> ObjectType for &T {}
152
153impl<T: ObjectType + ?Sized> ObjectType for Box<T> {}
154
155impl<T: ObjectType + ?Sized> ObjectType for Arc<T> {}
156
157/// A GraphQL interface.
158pub trait InterfaceType: ContainerType {}
159
160/// A GraphQL interface.
161pub trait UnionType: ContainerType {}
162
163/// A GraphQL input object.
164pub trait InputObjectType: InputType {}
165
166/// A GraphQL oneof input object.
167pub trait OneofObjectType: InputObjectType {}
168
169#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
170impl<T: OutputType + ?Sized> OutputType for Box<T> {
171    fn type_name() -> Cow<'static, str> {
172        T::type_name()
173    }
174
175    fn create_type_info(registry: &mut Registry) -> String {
176        T::create_type_info(registry)
177    }
178
179    #[cfg(feature = "boxed-trait")]
180    async fn resolve(
181        &self,
182        ctx: &ContextSelectionSet<'_>,
183        field: &Positioned<Field>,
184    ) -> ServerResult<Value> {
185        T::resolve(self.as_ref(), ctx, field).await
186    }
187
188    #[allow(clippy::trivially_copy_pass_by_ref)]
189    #[cfg(not(feature = "boxed-trait"))]
190    fn resolve(
191        &self,
192        ctx: &ContextSelectionSet<'_>,
193        field: &Positioned<Field>,
194    ) -> impl Future<Output = ServerResult<Value>> + Send {
195        T::resolve(self.as_ref(), ctx, field)
196    }
197}
198
199impl<T: InputType> InputType for Box<T> {
200    type RawValueType = T::RawValueType;
201
202    fn type_name() -> Cow<'static, str> {
203        T::type_name()
204    }
205
206    fn create_type_info(registry: &mut Registry) -> String {
207        T::create_type_info(registry)
208    }
209
210    fn parse(value: Option<ConstValue>) -> InputValueResult<Self> {
211        T::parse(value)
212            .map(Box::new)
213            .map_err(InputValueError::propagate)
214    }
215
216    fn to_value(&self) -> ConstValue {
217        T::to_value(&self)
218    }
219
220    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
221        self.as_ref().as_raw_value()
222    }
223}
224
225#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
226impl<T: OutputType + ?Sized> OutputType for Arc<T> {
227    fn type_name() -> Cow<'static, str> {
228        T::type_name()
229    }
230
231    fn create_type_info(registry: &mut Registry) -> String {
232        T::create_type_info(registry)
233    }
234
235    #[allow(clippy::trivially_copy_pass_by_ref)]
236    async fn resolve(
237        &self,
238        ctx: &ContextSelectionSet<'_>,
239        field: &Positioned<Field>,
240    ) -> ServerResult<Value> {
241        T::resolve(&**self, ctx, field).await
242    }
243}
244
245impl<T: InputType> InputType for Arc<T> {
246    type RawValueType = T::RawValueType;
247
248    fn type_name() -> Cow<'static, str> {
249        T::type_name()
250    }
251
252    fn create_type_info(registry: &mut Registry) -> String {
253        T::create_type_info(registry)
254    }
255
256    fn parse(value: Option<ConstValue>) -> InputValueResult<Self> {
257        T::parse(value)
258            .map(Arc::new)
259            .map_err(InputValueError::propagate)
260    }
261
262    fn to_value(&self) -> ConstValue {
263        T::to_value(&self)
264    }
265
266    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
267        self.as_ref().as_raw_value()
268    }
269}
270
271#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
272impl<T: OutputType + ?Sized> OutputType for Weak<T> {
273    fn type_name() -> Cow<'static, str> {
274        <Option<Arc<T>> as OutputType>::type_name()
275    }
276
277    fn create_type_info(registry: &mut Registry) -> String {
278        <Option<Arc<T>> as OutputType>::create_type_info(registry)
279    }
280
281    async fn resolve(
282        &self,
283        ctx: &ContextSelectionSet<'_>,
284        field: &Positioned<Field>,
285    ) -> ServerResult<Value> {
286        self.upgrade().resolve(ctx, field).await
287    }
288}
289
290#[doc(hidden)]
291#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
292pub trait ComplexObject {
293    fn fields(registry: &mut registry::Registry) -> Vec<(String, registry::MetaField)>;
294
295    #[cfg(feature = "boxed-trait")]
296    async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>>;
297
298    #[cfg(not(feature = "boxed-trait"))]
299    fn resolve_field(
300        &self,
301        ctx: &Context<'_>,
302    ) -> impl Future<Output = ServerResult<Option<Value>>> + Send;
303}