async_graphql/dynamic/
field.rs

1use std::{
2    any::Any,
3    borrow::Cow,
4    fmt::{self, Debug},
5    ops::Deref,
6};
7
8use futures_util::{future::BoxFuture, Future, FutureExt};
9use indexmap::IndexMap;
10
11use super::Directive;
12use crate::{
13    dynamic::{InputValue, ObjectAccessor, TypeRef},
14    registry::Deprecation,
15    Context, Error, Result, Value,
16};
17
18/// A value returned from the resolver function
19pub struct FieldValue<'a>(pub(crate) FieldValueInner<'a>);
20
21pub(crate) enum FieldValueInner<'a> {
22    /// Const value
23    Value(Value),
24    /// Borrowed any value
25    /// The first item is the [`std::any::type_name`] of the value used for
26    /// debugging.
27    BorrowedAny(Cow<'static, str>, &'a (dyn Any + Send + Sync)),
28    /// Owned any value
29    /// The first item is the [`std::any::type_name`] of the value used for
30    /// debugging.
31    OwnedAny(Cow<'static, str>, Box<dyn Any + Send + Sync>),
32    /// A list
33    List(Vec<FieldValue<'a>>),
34    /// A typed Field value
35    WithType {
36        /// Field value
37        value: Box<FieldValue<'a>>,
38        /// Object name
39        ty: Cow<'static, str>,
40    },
41}
42
43impl Debug for FieldValue<'_> {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        match &self.0 {
46            FieldValueInner::Value(v) => write!(f, "{}", v),
47            FieldValueInner::BorrowedAny(ty, _)
48            | FieldValueInner::OwnedAny(ty, _)
49            | FieldValueInner::WithType { ty, .. } => write!(f, "{}", ty),
50            FieldValueInner::List(list) => match list.first() {
51                Some(v) => {
52                    write!(f, "[{:?}, ...]", v)
53                }
54                None => {
55                    write!(f, "[()]")
56                }
57            },
58        }
59    }
60}
61
62impl From<()> for FieldValue<'_> {
63    #[inline]
64    fn from(_: ()) -> Self {
65        Self(FieldValueInner::Value(Value::Null))
66    }
67}
68
69impl From<Value> for FieldValue<'_> {
70    #[inline]
71    fn from(value: Value) -> Self {
72        Self(FieldValueInner::Value(value))
73    }
74}
75
76impl<'a, T: Into<FieldValue<'a>>> From<Vec<T>> for FieldValue<'a> {
77    fn from(values: Vec<T>) -> Self {
78        Self(FieldValueInner::List(
79            values.into_iter().map(Into::into).collect(),
80        ))
81    }
82}
83
84impl<'a> FieldValue<'a> {
85    /// A null value equivalent to `FieldValue::Value(Value::Null)`
86    pub const NULL: FieldValue<'a> = Self(FieldValueInner::Value(Value::Null));
87
88    /// A none value equivalent to `None::<FieldValue>`
89    ///
90    /// It is more convenient to use when your resolver needs to return `None`.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use async_graphql::dynamic::*;
96    ///
97    /// let query =
98    ///     Object::new("Query").field(Field::new("value", TypeRef::named(TypeRef::INT), |ctx| {
99    ///         FieldFuture::new(async move { Ok(FieldValue::NONE) })
100    ///     }));
101    /// ```
102    pub const NONE: Option<FieldValue<'a>> = None;
103
104    /// Returns a `None::<FieldValue>` meaning the resolver no results.
105    pub const fn none() -> Option<FieldValue<'a>> {
106        None
107    }
108
109    /// Create a FieldValue from [`Value`]
110    #[inline]
111    pub fn value(value: impl Into<Value>) -> Self {
112        Self(FieldValueInner::Value(value.into()))
113    }
114
115    /// Create a FieldValue from owned any value
116    #[inline]
117    pub fn owned_any<T: Any + Send + Sync>(obj: T) -> Self {
118        Self(FieldValueInner::OwnedAny(
119            std::any::type_name::<T>().into(),
120            Box::new(obj),
121        ))
122    }
123
124    /// Create a FieldValue from unsized any value
125    #[inline]
126    pub fn boxed_any(obj: Box<dyn Any + Send + Sync>) -> Self {
127        Self(FieldValueInner::OwnedAny("Any".into(), obj))
128    }
129
130    /// Create a FieldValue from owned any value
131    #[inline]
132    pub fn borrowed_any(obj: &'a (dyn Any + Send + Sync)) -> Self {
133        Self(FieldValueInner::BorrowedAny("Any".into(), obj))
134    }
135
136    /// Create a FieldValue from list
137    #[inline]
138    pub fn list<I, T>(values: I) -> Self
139    where
140        I: IntoIterator<Item = T>,
141        T: Into<FieldValue<'a>>,
142    {
143        Self(FieldValueInner::List(
144            values.into_iter().map(Into::into).collect(),
145        ))
146    }
147
148    /// Create a FieldValue and specify its type, which must be an object
149    ///
150    /// NOTE: Fields of type `Interface` or `Union` must return
151    /// `FieldValue::WithType`.
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use async_graphql::{dynamic::*, value, Value};
157    ///
158    /// struct MyObjData {
159    ///     a: i32,
160    /// }
161    ///
162    /// let my_obj = Object::new("MyObj").field(Field::new(
163    ///     "a",
164    ///     TypeRef::named_nn(TypeRef::INT),
165    ///     |ctx| FieldFuture::new(async move {
166    ///         let data = ctx.parent_value.try_downcast_ref::<MyObjData>()?;
167    ///         Ok(Some(Value::from(data.a)))
168    ///     }),
169    /// ));
170    ///
171    /// let my_union = Union::new("MyUnion").possible_type(my_obj.type_name());
172    ///
173    /// let query = Object::new("Query").field(Field::new(
174    ///     "obj",
175    ///     TypeRef::named_nn(my_union.type_name()),
176    ///     |_| FieldFuture::new(async move {
177    ///         Ok(Some(FieldValue::owned_any(MyObjData { a: 10 }).with_type("MyObj")))
178    ///     }),
179    /// ));
180    ///
181    /// let schema = Schema::build("Query", None, None)
182    ///     .register(my_obj)
183    ///     .register(my_union)
184    ///     .register(query)
185    ///     .finish()
186    ///     .unwrap();
187    ///
188    /// # tokio::runtime::Runtime::new().unwrap().block_on(async move {
189    /// assert_eq!(
190    ///    schema
191    ///        .execute("{ obj { ... on MyObj { a } } }")
192    ///        .await
193    ///        .into_result()
194    ///        .unwrap()
195    ///        .data,
196    ///    value!({ "obj": { "a": 10 } })
197    /// );
198    /// # });
199    /// ```
200    pub fn with_type(self, ty: impl Into<Cow<'static, str>>) -> Self {
201        Self(FieldValueInner::WithType {
202            value: Box::new(self),
203            ty: ty.into(),
204        })
205    }
206
207    /// If the FieldValue is a value, returns the associated
208    /// Value. Returns `None` otherwise.
209    #[inline]
210    pub fn as_value(&self) -> Option<&Value> {
211        match &self.0 {
212            FieldValueInner::Value(value) => Some(value),
213            _ => None,
214        }
215    }
216
217    /// Like `as_value`, but returns `Result`.
218    #[inline]
219    pub fn try_to_value(&self) -> Result<&Value> {
220        self.as_value()
221            .ok_or_else(|| Error::new(format!("internal: \"{:?}\" not a Value", self)))
222    }
223
224    /// If the FieldValue is a list, returns the associated
225    /// vector. Returns `None` otherwise.
226    #[inline]
227    pub fn as_list(&self) -> Option<&[FieldValue]> {
228        match &self.0 {
229            FieldValueInner::List(values) => Some(values),
230            _ => None,
231        }
232    }
233
234    /// Like `as_list`, but returns `Result`.
235    #[inline]
236    pub fn try_to_list(&self) -> Result<&[FieldValue]> {
237        self.as_list()
238            .ok_or_else(|| Error::new(format!("internal: \"{:?}\" not a List", self)))
239    }
240
241    /// If the FieldValue is a any, returns the associated
242    /// vector. Returns `None` otherwise.
243    #[inline]
244    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
245        match &self.0 {
246            FieldValueInner::BorrowedAny(_, value) => value.downcast_ref::<T>(),
247            FieldValueInner::OwnedAny(_, value) => value.downcast_ref::<T>(),
248            _ => None,
249        }
250    }
251
252    /// Like `downcast_ref`, but returns `Result`.
253    #[inline]
254    pub fn try_downcast_ref<T: Any>(&self) -> Result<&T> {
255        self.downcast_ref().ok_or_else(|| {
256            Error::new(format!(
257                "internal: \"{:?}\" is not of the expected type \"{}\"",
258                self,
259                std::any::type_name::<T>()
260            ))
261        })
262    }
263}
264
265type BoxResolveFut<'a> = BoxFuture<'a, Result<Option<FieldValue<'a>>>>;
266
267/// A context for resolver function
268pub struct ResolverContext<'a> {
269    /// GraphQL context
270    pub ctx: &'a Context<'a>,
271    /// Field arguments
272    pub args: ObjectAccessor<'a>,
273    /// Parent value
274    pub parent_value: &'a FieldValue<'a>,
275}
276
277impl<'a> Deref for ResolverContext<'a> {
278    type Target = Context<'a>;
279
280    fn deref(&self) -> &Self::Target {
281        self.ctx
282    }
283}
284
285/// A future that returned from field resolver
286pub enum FieldFuture<'a> {
287    /// A pure value without any async operation
288    Value(Option<FieldValue<'a>>),
289
290    /// A future that returned from field resolver
291    Future(BoxResolveFut<'a>),
292}
293
294impl<'a> FieldFuture<'a> {
295    /// Create a `FieldFuture` from a `Future`
296    pub fn new<Fut, R>(future: Fut) -> Self
297    where
298        Fut: Future<Output = Result<Option<R>>> + Send + 'a,
299        R: Into<FieldValue<'a>> + Send,
300    {
301        FieldFuture::Future(
302            async move {
303                let res = future.await?;
304                Ok(res.map(Into::into))
305            }
306            .boxed(),
307        )
308    }
309
310    /// Create a `FieldFuture` from a `Value`
311    pub fn from_value(value: Option<Value>) -> Self {
312        FieldFuture::Value(value.map(FieldValue::from))
313    }
314}
315
316pub(crate) type BoxResolverFn =
317    Box<(dyn for<'a> Fn(ResolverContext<'a>) -> FieldFuture<'a> + Send + Sync)>;
318
319/// A GraphQL field
320pub struct Field {
321    pub(crate) name: String,
322    pub(crate) description: Option<String>,
323    pub(crate) arguments: IndexMap<String, InputValue>,
324    pub(crate) ty: TypeRef,
325    pub(crate) ty_str: String,
326    pub(crate) resolver_fn: BoxResolverFn,
327    pub(crate) deprecation: Deprecation,
328    pub(crate) external: bool,
329    pub(crate) requires: Option<String>,
330    pub(crate) provides: Option<String>,
331    pub(crate) shareable: bool,
332    pub(crate) inaccessible: bool,
333    pub(crate) tags: Vec<String>,
334    pub(crate) override_from: Option<String>,
335    pub(crate) directives: Vec<Directive>,
336}
337
338impl Debug for Field {
339    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340        f.debug_struct("Field")
341            .field("name", &self.name)
342            .field("description", &self.description)
343            .field("arguments", &self.arguments)
344            .field("ty", &self.ty)
345            .field("deprecation", &self.deprecation)
346            .finish()
347    }
348}
349
350impl Field {
351    /// Create a GraphQL field
352    pub fn new<N, T, F>(name: N, ty: T, resolver_fn: F) -> Self
353    where
354        N: Into<String>,
355        T: Into<TypeRef>,
356        F: for<'a> Fn(ResolverContext<'a>) -> FieldFuture<'a> + Send + Sync + 'static,
357    {
358        let ty = ty.into();
359        Self {
360            name: name.into(),
361            description: None,
362            arguments: Default::default(),
363            ty_str: ty.to_string(),
364            ty,
365            resolver_fn: Box::new(resolver_fn),
366            deprecation: Deprecation::NoDeprecated,
367            external: false,
368            requires: None,
369            provides: None,
370            shareable: false,
371            inaccessible: false,
372            tags: Vec::new(),
373            override_from: None,
374            directives: Vec::new(),
375        }
376    }
377
378    impl_set_description!();
379    impl_set_deprecation!();
380    impl_set_external!();
381    impl_set_requires!();
382    impl_set_provides!();
383    impl_set_shareable!();
384    impl_set_inaccessible!();
385    impl_set_tags!();
386    impl_set_override_from!();
387    impl_directive!();
388
389    /// Add an argument to the field
390    #[inline]
391    pub fn argument(mut self, input_value: InputValue) -> Self {
392        self.arguments.insert(input_value.name.clone(), input_value);
393        self
394    }
395}