apollo_smith/
object.rs

1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::field::FieldDef;
5use crate::name::Name;
6use crate::DocumentBuilder;
7use crate::StackedEntity;
8use apollo_compiler::ast;
9use apollo_compiler::Node;
10use arbitrary::Result as ArbitraryResult;
11use indexmap::IndexMap;
12use indexmap::IndexSet;
13
14/// Object types represent concrete instantiations of sets of fields.
15///
16/// The introspection types (e.g. `__Type`, `__Field`, etc) are examples of
17/// objects.
18///
19/// *ObjectTypeDefinition*:
20///     Description? **type** Name ImplementsInterfaces? Directives? FieldsDefinition?
21///
22/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Object).
23#[derive(Debug, Clone)]
24pub struct ObjectTypeDef {
25    pub(crate) description: Option<Description>,
26    pub(crate) name: Name,
27    pub(crate) implements_interfaces: IndexSet<Name>,
28    pub(crate) directives: IndexMap<Name, Directive>,
29    pub(crate) fields_def: Vec<FieldDef>,
30    pub(crate) extend: bool,
31}
32
33impl From<ObjectTypeDef> for ast::Definition {
34    fn from(x: ObjectTypeDef) -> Self {
35        if x.extend {
36            ast::ObjectTypeExtension {
37                name: x.name.into(),
38                implements_interfaces: x
39                    .implements_interfaces
40                    .into_iter()
41                    .map(Into::into)
42                    .collect(),
43                directives: Directive::to_ast(x.directives),
44                fields: x
45                    .fields_def
46                    .into_iter()
47                    .map(|x| Node::new(x.into()))
48                    .collect(),
49            }
50            .into()
51        } else {
52            ast::ObjectTypeDefinition {
53                description: x.description.map(Into::into),
54                name: x.name.into(),
55                implements_interfaces: x
56                    .implements_interfaces
57                    .into_iter()
58                    .map(Into::into)
59                    .collect(),
60                directives: Directive::to_ast(x.directives),
61                fields: x
62                    .fields_def
63                    .into_iter()
64                    .map(|x| Node::new(x.into()))
65                    .collect(),
66            }
67            .into()
68        }
69    }
70}
71
72impl TryFrom<apollo_parser::cst::ObjectTypeDefinition> for ObjectTypeDef {
73    type Error = crate::FromError;
74
75    fn try_from(object_def: apollo_parser::cst::ObjectTypeDefinition) -> Result<Self, Self::Error> {
76        Ok(Self {
77            name: object_def
78                .name()
79                .expect("object type definition must have a name")
80                .into(),
81            description: object_def.description().map(Description::from),
82            directives: object_def
83                .directives()
84                .map(Directive::convert_directives)
85                .transpose()?
86                .unwrap_or_default(),
87            implements_interfaces: object_def
88                .implements_interfaces()
89                .map(|impl_int| {
90                    impl_int
91                        .named_types()
92                        .map(|n| n.name().unwrap().into())
93                        .collect()
94                })
95                .unwrap_or_default(),
96            extend: false,
97            fields_def: object_def
98                .fields_definition()
99                .expect("object type definition must have fields definition")
100                .field_definitions()
101                .map(FieldDef::try_from)
102                .collect::<Result<Vec<_>, _>>()?,
103        })
104    }
105}
106
107impl TryFrom<apollo_parser::cst::ObjectTypeExtension> for ObjectTypeDef {
108    type Error = crate::FromError;
109
110    fn try_from(object_def: apollo_parser::cst::ObjectTypeExtension) -> Result<Self, Self::Error> {
111        Ok(Self {
112            name: object_def
113                .name()
114                .expect("object type definition must have a name")
115                .into(),
116            description: None,
117            directives: object_def
118                .directives()
119                .map(Directive::convert_directives)
120                .transpose()?
121                .unwrap_or_default(),
122            implements_interfaces: object_def
123                .implements_interfaces()
124                .map(|impl_int| {
125                    impl_int
126                        .named_types()
127                        .map(|n| n.name().unwrap().into())
128                        .collect()
129                })
130                .unwrap_or_default(),
131            extend: true,
132            fields_def: object_def
133                .fields_definition()
134                .expect("object type definition must have fields definition")
135                .field_definitions()
136                .map(FieldDef::try_from)
137                .collect::<Result<Vec<_>, _>>()?,
138        })
139    }
140}
141
142impl DocumentBuilder<'_> {
143    /// Create an arbitrary `ObjectTypeDef`
144    pub fn object_type_definition(&mut self) -> ArbitraryResult<ObjectTypeDef> {
145        let extend = !self.object_type_defs.is_empty() && self.u.arbitrary().unwrap_or(false);
146        let description = self
147            .u
148            .arbitrary()
149            .unwrap_or(false)
150            .then(|| self.description())
151            .transpose()?;
152        let name = if extend {
153            let available_objects: Vec<&Name> = self
154                .object_type_defs
155                .iter()
156                .filter_map(|object| {
157                    if object.extend {
158                        None
159                    } else {
160                        Some(&object.name)
161                    }
162                })
163                .collect();
164            (*self.u.choose(&available_objects)?).clone()
165        } else {
166            self.type_name()?
167        };
168
169        // ---- Interface
170        let interface_impls = self.implements_interfaces()?;
171        let implements_fields: Vec<FieldDef> = interface_impls
172            .iter()
173            .flat_map(|itf_name| {
174                self.interface_type_defs
175                    .iter()
176                    .find(|itf| &itf.name == itf_name)
177                    .expect("cannot find the corresponding interface")
178                    .fields_def
179                    .clone()
180            })
181            .collect();
182
183        let mut fields_def = self.fields_definition(
184            &implements_fields
185                .iter()
186                .map(|f| &f.name)
187                .collect::<Vec<&Name>>(),
188        )?;
189        // Add fields coming from interfaces
190        fields_def.extend(implements_fields);
191
192        Ok(ObjectTypeDef {
193            description,
194            directives: self.directives(DirectiveLocation::Object)?,
195            implements_interfaces: interface_impls,
196            name,
197            fields_def,
198            extend,
199        })
200    }
201}
202
203impl StackedEntity for ObjectTypeDef {
204    fn name(&self) -> &Name {
205        &self.name
206    }
207
208    fn fields_def(&self) -> &[FieldDef] {
209        &self.fields_def
210    }
211}