async_graphql/types/
merged_object.rs

1use std::{borrow::Cow, pin::Pin};
2
3use indexmap::IndexMap;
4
5use crate::{
6    CacheControl, ContainerType, Context, ContextSelectionSet, OutputType, Positioned, Response,
7    ServerResult, SimpleObject, SubscriptionType, Value,
8    futures_util::stream::Stream,
9    parser::types::Field,
10    registry::{MetaType, MetaTypeId, Registry},
11};
12
13#[doc(hidden)]
14pub struct MergedObject<A, B>(pub A, pub B);
15
16#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
17impl<A, B> ContainerType for MergedObject<A, B>
18where
19    A: ContainerType,
20    B: ContainerType,
21{
22    async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
23        match self.0.resolve_field(ctx).await {
24            Ok(Some(value)) => Ok(Some(value)),
25            Ok(None) => self.1.resolve_field(ctx).await,
26            Err(err) => Err(err),
27        }
28    }
29
30    async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
31        match self.0.find_entity(ctx, params).await {
32            Ok(Some(value)) => Ok(Some(value)),
33            Ok(None) => self.1.find_entity(ctx, params).await,
34            Err(err) => Err(err),
35        }
36    }
37}
38
39#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
40impl<A, B> OutputType for MergedObject<A, B>
41where
42    A: OutputType,
43    B: OutputType,
44{
45    fn type_name() -> Cow<'static, str> {
46        Cow::Owned(format!("{}_{}", A::type_name(), B::type_name()))
47    }
48
49    fn create_type_info(registry: &mut Registry) -> String {
50        registry.create_output_type::<Self, _>(MetaTypeId::Object, |registry| {
51            let mut fields = IndexMap::new();
52            let mut cc = CacheControl::default();
53
54            if let MetaType::Object {
55                fields: b_fields,
56                cache_control: b_cc,
57                ..
58            } = registry.create_fake_output_type::<B>()
59            {
60                fields.extend(b_fields);
61                cc = cc.merge(&b_cc);
62            }
63
64            if let MetaType::Object {
65                fields: a_fields,
66                cache_control: a_cc,
67                ..
68            } = registry.create_fake_output_type::<A>()
69            {
70                fields.extend(a_fields);
71                cc = cc.merge(&a_cc);
72            }
73
74            MetaType::Object {
75                name: Self::type_name().to_string(),
76                description: None,
77                fields,
78                cache_control: cc,
79                extends: false,
80                shareable: false,
81                resolvable: true,
82                keys: None,
83                visible: None,
84                inaccessible: false,
85                interface_object: false,
86                tags: Default::default(),
87                is_subscription: false,
88                rust_typename: Some(std::any::type_name::<Self>()),
89                directive_invocations: Default::default(),
90                requires_scopes: Default::default(),
91            }
92        })
93    }
94
95    async fn resolve(
96        &self,
97        _ctx: &ContextSelectionSet<'_>,
98        _field: &Positioned<Field>,
99    ) -> ServerResult<Value> {
100        unreachable!()
101    }
102}
103
104impl<A, B> SubscriptionType for MergedObject<A, B>
105where
106    A: SubscriptionType,
107    B: SubscriptionType,
108{
109    fn type_name() -> Cow<'static, str> {
110        Cow::Owned(format!("{}_{}", A::type_name(), B::type_name()))
111    }
112
113    fn create_type_info(registry: &mut Registry) -> String {
114        registry.create_subscription_type::<Self, _>(|registry| {
115            let mut fields = IndexMap::new();
116            let mut cc = CacheControl::default();
117
118            if let MetaType::Object {
119                fields: b_fields,
120                cache_control: b_cc,
121                ..
122            } = registry.create_fake_subscription_type::<B>()
123            {
124                fields.extend(b_fields);
125                cc = cc.merge(&b_cc);
126            }
127
128            if let MetaType::Object {
129                fields: a_fields,
130                cache_control: a_cc,
131                ..
132            } = registry.create_fake_subscription_type::<A>()
133            {
134                fields.extend(a_fields);
135                cc = cc.merge(&a_cc);
136            }
137
138            MetaType::Object {
139                name: Self::type_name().to_string(),
140                description: None,
141                fields,
142                cache_control: cc,
143                extends: false,
144                shareable: false,
145                resolvable: true,
146                keys: None,
147                visible: None,
148                inaccessible: false,
149                interface_object: false,
150                tags: Default::default(),
151                is_subscription: false,
152                rust_typename: Some(std::any::type_name::<Self>()),
153                directive_invocations: Default::default(),
154                requires_scopes: Default::default(),
155            }
156        })
157    }
158
159    fn create_field_stream<'a>(
160        &'a self,
161        _ctx: &'a Context<'_>,
162    ) -> Option<Pin<Box<dyn Stream<Item = Response> + Send + 'a>>> {
163        unreachable!()
164    }
165}
166
167#[doc(hidden)]
168#[derive(SimpleObject, Default)]
169#[graphql(internal, fake)]
170pub struct MergedObjectTail;
171
172impl SubscriptionType for MergedObjectTail {
173    fn type_name() -> Cow<'static, str> {
174        Cow::Borrowed("MergedSubscriptionTail")
175    }
176
177    fn create_type_info(registry: &mut Registry) -> String {
178        registry.create_subscription_type::<Self, _>(|_| MetaType::Object {
179            name: "MergedSubscriptionTail".to_string(),
180            description: None,
181            fields: Default::default(),
182            cache_control: Default::default(),
183            extends: false,
184            shareable: false,
185            resolvable: true,
186            keys: None,
187            visible: None,
188            inaccessible: false,
189            interface_object: false,
190            tags: Default::default(),
191            is_subscription: false,
192            rust_typename: Some(std::any::type_name::<Self>()),
193            directive_invocations: Default::default(),
194            requires_scopes: Default::default(),
195        })
196    }
197
198    fn create_field_stream<'a>(
199        &'a self,
200        _ctx: &'a Context<'_>,
201    ) -> Option<Pin<Box<dyn Stream<Item = Response> + Send + 'a>>> {
202        unreachable!()
203    }
204}