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