apollo_smith/
input_object.rs

1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::input_value::InputValueDef;
5use crate::name::Name;
6use crate::DocumentBuilder;
7use apollo_compiler::ast;
8use apollo_compiler::Node;
9use arbitrary::Result as ArbitraryResult;
10use indexmap::IndexMap;
11
12/// Input objects are composite types used as inputs into queries defined as a list of named input values..
13///
14/// InputObjectTypeDefinition
15///     Description? **input** Name Directives? FieldsDefinition?
16///
17/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Input-Objects).
18///
19/// **Note**: At the moment InputObjectTypeDefinition differs slightly from the
20/// spec. Instead of accepting InputValues as `field` parameter, we accept
21/// InputField.
22#[derive(Debug, Clone)]
23pub struct InputObjectTypeDef {
24    pub(crate) name: Name,
25    pub(crate) description: Option<Description>,
26    // A vector of fields
27    pub(crate) fields: Vec<InputValueDef>,
28    /// Contains all directives.
29    pub(crate) directives: IndexMap<Name, Directive>,
30    pub(crate) extend: bool,
31}
32
33impl From<InputObjectTypeDef> for ast::Definition {
34    fn from(x: InputObjectTypeDef) -> Self {
35        if x.extend {
36            ast::InputObjectTypeExtension {
37                name: x.name.into(),
38                directives: Directive::to_ast(x.directives),
39                fields: x.fields.into_iter().map(|x| Node::new(x.into())).collect(),
40            }
41            .into()
42        } else {
43            ast::InputObjectTypeDefinition {
44                description: x.description.map(Into::into),
45                name: x.name.into(),
46                directives: Directive::to_ast(x.directives),
47                fields: x.fields.into_iter().map(|x| Node::new(x.into())).collect(),
48            }
49            .into()
50        }
51    }
52}
53
54impl TryFrom<apollo_parser::cst::InputObjectTypeDefinition> for InputObjectTypeDef {
55    type Error = crate::FromError;
56
57    fn try_from(
58        input_object: apollo_parser::cst::InputObjectTypeDefinition,
59    ) -> Result<Self, Self::Error> {
60        Ok(Self {
61            name: input_object
62                .name()
63                .expect("object type definition must have a name")
64                .into(),
65            description: input_object.description().map(Description::from),
66            directives: input_object
67                .directives()
68                .map(Directive::convert_directives)
69                .transpose()?
70                .unwrap_or_default(),
71            extend: false,
72            fields: input_object
73                .input_fields_definition()
74                .map(|input_fields| {
75                    input_fields
76                        .input_value_definitions()
77                        .map(InputValueDef::try_from)
78                        .collect::<Result<_, _>>()
79                })
80                .transpose()?
81                .unwrap_or_default(),
82        })
83    }
84}
85
86impl TryFrom<apollo_parser::cst::InputObjectTypeExtension> for InputObjectTypeDef {
87    type Error = crate::FromError;
88
89    fn try_from(
90        input_object: apollo_parser::cst::InputObjectTypeExtension,
91    ) -> Result<Self, Self::Error> {
92        Ok(Self {
93            name: input_object
94                .name()
95                .expect("object type definition must have a name")
96                .into(),
97            directives: input_object
98                .directives()
99                .map(Directive::convert_directives)
100                .transpose()?
101                .unwrap_or_default(),
102            extend: true,
103            fields: input_object
104                .input_fields_definition()
105                .map(|input_fields| {
106                    input_fields
107                        .input_value_definitions()
108                        .map(InputValueDef::try_from)
109                        .collect::<Result<Vec<_>, crate::FromError>>()
110                })
111                .transpose()?
112                .unwrap_or_default(),
113            description: None,
114        })
115    }
116}
117
118impl DocumentBuilder<'_> {
119    /// Create an arbitrary `InputObjectTypeDef`
120    pub fn input_object_type_definition(&mut self) -> ArbitraryResult<InputObjectTypeDef> {
121        let extend = !self.input_object_type_defs.is_empty() && self.u.arbitrary().unwrap_or(false);
122        let name = if extend {
123            let available_input_objects: Vec<&Name> = self
124                .input_object_type_defs
125                .iter()
126                .filter_map(|input_object| {
127                    if input_object.extend {
128                        None
129                    } else {
130                        Some(&input_object.name)
131                    }
132                })
133                .collect();
134            (*self.u.choose(&available_input_objects)?).clone()
135        } else {
136            self.type_name()?
137        };
138        let description = self
139            .u
140            .arbitrary()
141            .unwrap_or(false)
142            .then(|| self.description())
143            .transpose()?;
144        let fields = self.input_values_def()?;
145
146        Ok(InputObjectTypeDef {
147            description,
148            directives: self.directives(DirectiveLocation::InputObject)?,
149            name,
150            extend,
151            fields,
152        })
153    }
154}