1use indexmap::{IndexMap, IndexSet};
2
3use super::{Directive, directive::to_meta_directive_invocation};
4use crate::{
5 dynamic::{Field, SchemaError},
6 registry::{MetaField, MetaType, Registry},
7};
8
9#[derive(Debug)]
40pub struct Object {
41 pub(crate) name: String,
42 pub(crate) description: Option<String>,
43 pub(crate) fields: IndexMap<String, Field>,
44 pub(crate) implements: IndexSet<String>,
45 keys: Vec<String>,
46 extends: bool,
47 shareable: bool,
48 resolvable: bool,
49 inaccessible: bool,
50 interface_object: bool,
51 tags: Vec<String>,
52 pub(crate) directives: Vec<Directive>,
53 requires_scopes: Vec<String>,
54}
55
56impl Object {
57 #[inline]
59 pub fn new(name: impl Into<String>) -> Self {
60 Self {
61 name: name.into(),
62 description: None,
63 fields: Default::default(),
64 implements: Default::default(),
65 keys: Vec::new(),
66 extends: false,
67 shareable: false,
68 resolvable: true,
69 inaccessible: false,
70 interface_object: false,
71 tags: Vec::new(),
72 directives: Vec::new(),
73 requires_scopes: Vec::new(),
74 }
75 }
76
77 impl_set_description!();
78 impl_set_extends!();
79 impl_set_shareable!();
80 impl_set_inaccessible!();
81 impl_set_interface_object!();
82 impl_set_tags!();
83 impl_directive!();
84
85 #[inline]
87 pub fn field(mut self, field: Field) -> Self {
88 assert!(
89 !self.fields.contains_key(&field.name),
90 "Field `{}` already exists",
91 field.name
92 );
93 self.fields.insert(field.name.clone(), field);
94 self
95 }
96
97 #[inline]
99 pub fn implement(mut self, interface: impl Into<String>) -> Self {
100 let interface = interface.into();
101 assert!(
102 !self.implements.contains(&interface),
103 "Implement `{}` already exists",
104 interface
105 );
106 self.implements.insert(interface);
107 self
108 }
109
110 pub fn key(mut self, fields: impl Into<String>) -> Self {
131 self.keys.push(fields.into());
132 self
133 }
134
135 pub fn unresolvable(mut self, fields: impl Into<String>) -> Self {
154 self.resolvable = false;
155 self.keys.push(fields.into());
156 self
157 }
158
159 #[inline]
161 pub fn type_name(&self) -> &str {
162 &self.name
163 }
164
165 pub(crate) fn register(&self, registry: &mut Registry) -> Result<(), SchemaError> {
166 let mut fields = IndexMap::new();
167
168 for field in self.fields.values() {
169 let mut args = IndexMap::new();
170
171 for argument in field.arguments.values() {
172 args.insert(argument.name.clone(), argument.to_meta_input_value());
173 }
174
175 fields.insert(
176 field.name.clone(),
177 MetaField {
178 name: field.name.clone(),
179 description: field.description.clone(),
180 args,
181 ty: field.ty.to_string(),
182 deprecation: field.deprecation.clone(),
183 cache_control: Default::default(),
184 external: field.external,
185 requires: field.requires.clone(),
186 provides: field.provides.clone(),
187 visible: None,
188 shareable: field.shareable,
189 inaccessible: field.inaccessible,
190 tags: field.tags.clone(),
191 override_from: field.override_from.clone(),
192 compute_complexity: None,
193 directive_invocations: to_meta_directive_invocation(field.directives.clone()),
194 requires_scopes: field.requires_scopes.clone(),
195 },
196 );
197 }
198
199 registry.types.insert(
200 self.name.clone(),
201 MetaType::Object {
202 name: self.name.clone(),
203 description: self.description.clone(),
204 fields,
205 cache_control: Default::default(),
206 extends: self.extends,
207 shareable: self.shareable,
208 resolvable: self.resolvable,
209 keys: if !self.keys.is_empty() {
210 Some(self.keys.clone())
211 } else {
212 None
213 },
214 visible: None,
215 inaccessible: self.inaccessible,
216 interface_object: self.interface_object,
217 tags: self.tags.clone(),
218 is_subscription: false,
219 rust_typename: None,
220 directive_invocations: to_meta_directive_invocation(self.directives.clone()),
221 requires_scopes: self.requires_scopes.clone(),
222 },
223 );
224
225 for interface in &self.implements {
226 registry.add_implements(&self.name, interface);
227 }
228
229 Ok(())
230 }
231
232 #[inline]
233 pub(crate) fn is_entity(&self) -> bool {
234 !self.keys.is_empty()
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use crate::{Value, dynamic::*, value};
241
242 #[tokio::test]
243 async fn borrow_context() {
244 struct MyObjData {
245 value: i32,
246 }
247
248 let my_obj =
249 Object::new("MyObj").field(Field::new("value", TypeRef::named(TypeRef::INT), |ctx| {
250 FieldFuture::new(async move {
251 Ok(Some(Value::from(
252 ctx.parent_value.try_downcast_ref::<MyObjData>()?.value,
253 )))
254 })
255 }));
256
257 let query = Object::new("Query").field(Field::new(
258 "obj",
259 TypeRef::named_nn(my_obj.type_name()),
260 |ctx| {
261 FieldFuture::new(async move {
262 Ok(Some(FieldValue::borrowed_any(
263 ctx.data_unchecked::<MyObjData>(),
264 )))
265 })
266 },
267 ));
268
269 let schema = Schema::build("Query", None, None)
270 .register(query)
271 .register(my_obj)
272 .data(MyObjData { value: 123 })
273 .finish()
274 .unwrap();
275
276 assert_eq!(
277 schema
278 .execute("{ obj { value } }")
279 .await
280 .into_result()
281 .unwrap()
282 .data,
283 value!({
284 "obj": {
285 "value": 123,
286 }
287 })
288 );
289 }
290}