apollo_smith/
interface.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/// InterfaceTypeDef is an abstract type where there are common fields declared.
15///
16/// Any type that implements an interface must define all the fields with names
17/// and types exactly matching. The implementations of this interface are
18/// explicitly listed out in possibleTypes.
19///
20/// *InterfaceTypeDefinition*:
21///     Description? **interface** Name ImplementsInterfaces? Directives?
22///
23/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#InterfaceTypeDefinition).
24#[derive(Debug, Clone)]
25pub struct InterfaceTypeDef {
26    pub(crate) description: Option<Description>,
27    pub(crate) name: Name,
28    pub(crate) interfaces: IndexSet<Name>,
29    pub(crate) directives: IndexMap<Name, Directive>,
30    pub(crate) fields_def: Vec<FieldDef>,
31    pub(crate) extend: bool,
32}
33
34impl From<InterfaceTypeDef> for ast::Definition {
35    fn from(x: InterfaceTypeDef) -> Self {
36        if x.extend {
37            ast::InterfaceTypeExtension {
38                name: x.name.into(),
39                implements_interfaces: x.interfaces.into_iter().map(Into::into).collect(),
40                directives: Directive::to_ast(x.directives),
41                fields: x
42                    .fields_def
43                    .into_iter()
44                    .map(|x| Node::new(x.into()))
45                    .collect(),
46            }
47            .into()
48        } else {
49            ast::InterfaceTypeDefinition {
50                description: x.description.map(Into::into),
51                name: x.name.into(),
52                implements_interfaces: x.interfaces.into_iter().map(Into::into).collect(),
53                directives: Directive::to_ast(x.directives),
54                fields: x
55                    .fields_def
56                    .into_iter()
57                    .map(|x| Node::new(x.into()))
58                    .collect(),
59            }
60            .into()
61        }
62    }
63}
64
65impl TryFrom<apollo_parser::cst::InterfaceTypeDefinition> for InterfaceTypeDef {
66    type Error = crate::FromError;
67
68    fn try_from(
69        interface_def: apollo_parser::cst::InterfaceTypeDefinition,
70    ) -> Result<Self, Self::Error> {
71        Ok(Self {
72            name: interface_def
73                .name()
74                .expect("object type definition must have a name")
75                .into(),
76            description: interface_def.description().map(Description::from),
77            directives: interface_def
78                .directives()
79                .map(Directive::convert_directives)
80                .transpose()?
81                .unwrap_or_default(),
82            extend: false,
83            fields_def: interface_def
84                .fields_definition()
85                .expect("object type definition must have fields definition")
86                .field_definitions()
87                .map(FieldDef::try_from)
88                .collect::<Result<Vec<_>, _>>()?,
89            interfaces: interface_def
90                .implements_interfaces()
91                .map(|itfs| {
92                    itfs.named_types()
93                        .map(|named_type| named_type.name().unwrap().into())
94                        .collect()
95                })
96                .unwrap_or_default(),
97        })
98    }
99}
100
101impl TryFrom<apollo_parser::cst::InterfaceTypeExtension> for InterfaceTypeDef {
102    type Error = crate::FromError;
103
104    fn try_from(
105        interface_def: apollo_parser::cst::InterfaceTypeExtension,
106    ) -> Result<Self, Self::Error> {
107        Ok(Self {
108            name: interface_def
109                .name()
110                .expect("object type definition must have a name")
111                .into(),
112            description: None,
113            directives: interface_def
114                .directives()
115                .map(Directive::convert_directives)
116                .transpose()?
117                .unwrap_or_default(),
118            extend: true,
119            fields_def: interface_def
120                .fields_definition()
121                .expect("object type definition must have fields definition")
122                .field_definitions()
123                .map(FieldDef::try_from)
124                .collect::<Result<Vec<_>, _>>()?,
125            interfaces: interface_def
126                .implements_interfaces()
127                .map(|itfs| {
128                    itfs.named_types()
129                        .map(|named_type| named_type.name().unwrap().into())
130                        .collect()
131                })
132                .unwrap_or_default(),
133        })
134    }
135}
136
137impl DocumentBuilder<'_> {
138    /// Create an arbitrary `InterfaceTypeDef`
139    pub fn interface_type_definition(&mut self) -> ArbitraryResult<InterfaceTypeDef> {
140        let extend = !self.interface_type_defs.is_empty() && self.u.arbitrary().unwrap_or(false);
141        let description = self
142            .u
143            .arbitrary()
144            .unwrap_or(false)
145            .then(|| self.description())
146            .transpose()?;
147        let name = if extend {
148            let available_itfs: Vec<&Name> = self
149                .interface_type_defs
150                .iter()
151                .filter_map(|itf| if itf.extend { None } else { Some(&itf.name) })
152                .collect();
153            (*self.u.choose(&available_itfs)?).clone()
154        } else {
155            self.type_name()?
156        };
157        let fields_def = self.fields_definition(&[])?;
158        let directives = self.directives(DirectiveLocation::Interface)?;
159        let interfaces = self.implements_interfaces()?;
160
161        Ok(InterfaceTypeDef {
162            description,
163            name,
164            fields_def,
165            directives,
166            extend,
167            interfaces,
168        })
169    }
170
171    /// Create an arbitrary `IndexSet` of implemented interfaces
172    pub fn implements_interfaces(&mut self) -> ArbitraryResult<IndexSet<Name>> {
173        if self.interface_type_defs.is_empty() {
174            return Ok(IndexSet::new());
175        }
176
177        let num_itf = self
178            .u
179            .int_in_range(0..=(self.interface_type_defs.len() - 1))?;
180        let mut interface_impls = IndexSet::with_capacity(num_itf);
181
182        for _ in 0..num_itf {
183            interface_impls.insert(self.u.choose(&self.interface_type_defs)?.name.clone());
184        }
185
186        Ok(interface_impls)
187    }
188}
189
190impl StackedEntity for InterfaceTypeDef {
191    fn name(&self) -> &Name {
192        &self.name
193    }
194
195    fn fields_def(&self) -> &[FieldDef] {
196        &self.fields_def
197    }
198}