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}