Skip to main content

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;
13use std::collections::HashMap;
14
15/// InterfaceTypeDef is an abstract type where there are common fields declared.
16///
17/// Any type that implements an interface must define all the fields with names
18/// and types exactly matching. The implementations of this interface are
19/// explicitly listed out in possibleTypes.
20///
21/// *InterfaceTypeDefinition*:
22///     Description? **interface** Name ImplementsInterfaces? Directives?
23///
24/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#InterfaceTypeDefinition).
25#[derive(Debug, Clone)]
26pub struct InterfaceTypeDef {
27    pub(crate) description: Option<Description>,
28    pub(crate) name: Name,
29    pub(crate) interfaces: IndexSet<Name>,
30    pub(crate) directives: IndexMap<Name, Directive>,
31    pub(crate) fields_def: Vec<FieldDef>,
32    pub(crate) extend: bool,
33}
34
35impl From<InterfaceTypeDef> for ast::Definition {
36    fn from(x: InterfaceTypeDef) -> Self {
37        if x.extend {
38            ast::InterfaceTypeExtension {
39                name: x.name.into(),
40                implements_interfaces: x.interfaces.into_iter().map(Into::into).collect(),
41                directives: Directive::to_ast(x.directives),
42                fields: x
43                    .fields_def
44                    .into_iter()
45                    .map(|x| Node::new(x.into()))
46                    .collect(),
47            }
48            .into()
49        } else {
50            ast::InterfaceTypeDefinition {
51                description: x.description.map(Into::into),
52                name: x.name.into(),
53                implements_interfaces: x.interfaces.into_iter().map(Into::into).collect(),
54                directives: Directive::to_ast(x.directives),
55                fields: x
56                    .fields_def
57                    .into_iter()
58                    .map(|x| Node::new(x.into()))
59                    .collect(),
60            }
61            .into()
62        }
63    }
64}
65
66impl TryFrom<apollo_parser::cst::InterfaceTypeDefinition> for InterfaceTypeDef {
67    type Error = crate::FromError;
68
69    fn try_from(
70        interface_def: apollo_parser::cst::InterfaceTypeDefinition,
71    ) -> Result<Self, Self::Error> {
72        Ok(Self {
73            name: interface_def
74                .name()
75                .expect("object type definition must have a name")
76                .into(),
77            description: interface_def.description().map(Description::from),
78            directives: interface_def
79                .directives()
80                .map(Directive::convert_directives)
81                .transpose()?
82                .unwrap_or_default(),
83            extend: false,
84            fields_def: interface_def
85                .fields_definition()
86                .expect("object type definition must have fields definition")
87                .field_definitions()
88                .map(FieldDef::try_from)
89                .collect::<Result<Vec<_>, _>>()?,
90            interfaces: interface_def
91                .implements_interfaces()
92                .map(|itfs| {
93                    itfs.named_types()
94                        .map(|named_type| named_type.name().unwrap().into())
95                        .collect()
96                })
97                .unwrap_or_default(),
98        })
99    }
100}
101
102impl TryFrom<apollo_parser::cst::InterfaceTypeExtension> for InterfaceTypeDef {
103    type Error = crate::FromError;
104
105    fn try_from(
106        interface_def: apollo_parser::cst::InterfaceTypeExtension,
107    ) -> Result<Self, Self::Error> {
108        Ok(Self {
109            name: interface_def
110                .name()
111                .expect("object type definition must have a name")
112                .into(),
113            description: None,
114            directives: interface_def
115                .directives()
116                .map(Directive::convert_directives)
117                .transpose()?
118                .unwrap_or_default(),
119            extend: true,
120            fields_def: interface_def
121                .fields_definition()
122                .expect("object type definition must have fields definition")
123                .field_definitions()
124                .map(FieldDef::try_from)
125                .collect::<Result<Vec<_>, _>>()?,
126            interfaces: interface_def
127                .implements_interfaces()
128                .map(|itfs| {
129                    itfs.named_types()
130                        .map(|named_type| named_type.name().unwrap().into())
131                        .collect()
132                })
133                .unwrap_or_default(),
134        })
135    }
136}
137
138impl DocumentBuilder<'_> {
139    /// Create an arbitrary `InterfaceTypeDef`
140    pub fn interface_type_definition(&mut self) -> ArbitraryResult<InterfaceTypeDef> {
141        let extend = !self.interface_type_defs.is_empty() && self.u.arbitrary().unwrap_or(false);
142        let description = self
143            .u
144            .arbitrary()
145            .unwrap_or(false)
146            .then(|| self.description())
147            .transpose()?;
148        let name = if extend {
149            let available_itfs: Vec<&Name> = self
150                .interface_type_defs
151                .iter()
152                .filter_map(|itf| if itf.extend { None } else { Some(&itf.name) })
153                .collect();
154            (*self.u.choose(&available_itfs)?).clone()
155        } else {
156            self.type_name()?
157        };
158        // Extensions can add new `implements` clauses, but must not
159        // duplicate prior ones, form a cycle, or overwrite a field
160        // signature the type already commits to. `additional_implements`
161        // enforces all three given the existing signatures.
162        let existing_field_signatures = field_signatures_for(&self.interface_type_defs, &name);
163        let interfaces = self.additional_implements(&existing_field_signatures, Some(&name))?;
164        let exclude_fields: IndexSet<Name> = existing_field_signatures
165            .keys()
166            .map(|k| Name::new(k.clone()))
167            .collect();
168        let fields_def = self.fields_definition(&exclude_fields)?;
169        let directives = self.directives(DirectiveLocation::Interface)?;
170
171        if extend && directives.is_empty() && fields_def.is_empty() && interfaces.is_empty() {
172            return Err(arbitrary::Error::IncorrectFormat);
173        }
174
175        Ok(InterfaceTypeDef {
176            description,
177            name,
178            fields_def,
179            directives,
180            extend,
181            interfaces,
182        })
183    }
184
185    /// Pick a random set of interfaces to implement, expanded to its
186    /// transitive closure so the validator's "must also implement"
187    /// rule is satisfied. A candidate is dropped if its closure would
188    /// conflict with parents already picked.
189    pub fn implements_interfaces(&mut self) -> ArbitraryResult<IndexSet<Name>> {
190        self.additional_implements(&IndexMap::new(), None)
191    }
192
193    /// Pick interfaces to add to an existing `implements` clause.
194    ///
195    /// - `existing_field_signatures`: field signatures the type
196    ///   already commits to. A pick whose closure would overwrite any
197    ///   of them is rejected.
198    /// - `self_name`: the type's own name when set, so a candidate
199    ///   whose closure loops back at this type is rejected.
200    ///
201    /// Returns only the newly accepted interfaces and their transitive
202    /// parents.
203    pub(crate) fn additional_implements(
204        &mut self,
205        existing_field_signatures: &IndexMap<String, FieldDef>,
206        self_name: Option<&Name>,
207    ) -> ArbitraryResult<IndexSet<Name>> {
208        if self.interface_type_defs.is_empty() {
209            return Ok(IndexSet::new());
210        }
211        let num_itf = self
212            .u
213            .int_in_range(0..=(self.interface_type_defs.len() - 1))?;
214        let fields_by_type_name = fields_from_all_definitions(&self.interface_type_defs);
215
216        let already_implemented_parents = match self_name {
217            Some(n) => self.implements_graph.direct_parents(n),
218            None => IndexSet::new(),
219        };
220        let mut accepted = already_implemented_parents.clone();
221        // Seed the conflict guard with each existing parent's fields so a
222        // new candidate whose closure carries a clashing signature is
223        // rejected before it joins `accepted`.
224        let mut accumulated_signatures = existing_field_signatures.clone();
225        for parent in &already_implemented_parents {
226            if let Some(fields) = fields_by_type_name.get(parent) {
227                for (fname, fdef) in fields {
228                    accumulated_signatures
229                        .entry(fname.clone())
230                        .or_insert_with(|| fdef.clone());
231                }
232            }
233        }
234
235        for _ in 0..num_itf {
236            let candidate = self.u.choose(&self.interface_type_defs)?.name.clone();
237            try_accept_candidate(
238                &candidate,
239                &self.implements_graph,
240                &fields_by_type_name,
241                &mut accepted,
242                &mut accumulated_signatures,
243                self_name,
244            );
245        }
246
247        accepted.retain(|n| !already_implemented_parents.contains(n));
248        Ok(accepted)
249    }
250
251    /// Reconcile each interface's `implements` clause and fields once
252    /// every interface is generated. Topological order means each
253    /// child reads only its direct parents — those parents already
254    /// hold their own inherited fields, so transitive fields flow
255    /// down without re-walking the closure per child.
256    pub(crate) fn backfill_inherited_interface_fields(&mut self) {
257        let order = self.implements_graph.topo_order_parents_first();
258        for name in order {
259            let Some(base_idx) = base_def_index(&self.interface_type_defs, &name) else {
260                continue;
261            };
262            self.expand_transitive_interface_implementations(&name, base_idx);
263
264            let parents = self.implements_graph.direct_parents(&name);
265            let mut inherited_fields = parent_fields_from_defs(&parents, &self.interface_type_defs);
266            // Rewrite child-declared fields to the parent's signature so a
267            // child's locally-picked type/args don't drift from the
268            // interface contract (covariance + matching args).
269            for i in def_indices_with_name(&self.interface_type_defs, &name) {
270                for f in self.interface_type_defs[i].fields_def.iter_mut() {
271                    if let Some(parent_fdef) = inherited_fields.shift_remove(&f.name.name) {
272                        *f = parent_fdef;
273                    }
274                }
275            }
276            // Append the parent fields the child never declared.
277            self.interface_type_defs[base_idx]
278                .fields_def
279                .extend(inherited_fields.into_values());
280        }
281    }
282
283    /// Write `name`'s transitive implementations onto its base def, skipping any already declared by an extension.
284    fn expand_transitive_interface_implementations(&mut self, name: &Name, base_idx: usize) {
285        let mut all_implemented_interfaces = self.implements_graph.closure(name);
286        // Closure includes `name`, but we don't want to write `interface X implements X`
287        all_implemented_interfaces.shift_remove(name);
288
289        let interfaces_declared_by_extensions: IndexSet<Name> = self
290            .interface_type_defs
291            .iter()
292            .filter(|i| i.extend && &i.name == name)
293            .flat_map(|i| i.interfaces.iter().cloned())
294            .collect();
295        let interfaces_to_add = all_implemented_interfaces
296            .into_iter()
297            .filter(|p| !interfaces_declared_by_extensions.contains(p));
298        self.interface_type_defs[base_idx]
299            .interfaces
300            .extend(interfaces_to_add);
301    }
302}
303
304/// Defs with a name and field list. Used by the backfill helpers so
305/// both `InterfaceTypeDef` and `ObjectTypeDef` can share them.
306pub(crate) trait NamedDef {
307    fn name(&self) -> &Name;
308    fn is_extend(&self) -> bool;
309    fn fields(&self) -> &[FieldDef];
310}
311
312impl NamedDef for InterfaceTypeDef {
313    fn name(&self) -> &Name {
314        &self.name
315    }
316    fn is_extend(&self) -> bool {
317        self.extend
318    }
319    fn fields(&self) -> &[FieldDef] {
320        &self.fields_def
321    }
322}
323
324impl NamedDef for crate::ObjectTypeDef {
325    fn name(&self) -> &Name {
326        &self.name
327    }
328    fn is_extend(&self) -> bool {
329        self.extend
330    }
331    fn fields(&self) -> &[FieldDef] {
332        &self.fields_def
333    }
334}
335
336/// Try to accept `candidate` (and every interface it transitively
337/// implements) into the implementer's `accepted` set.
338///
339/// Rejects when:
340/// - The closure loops back at the implementer (would produce
341///   `X implements ... implements X`).
342/// - Any field in the closure clashes with a signature already
343///   committed to in `accumulated_signatures`.
344fn try_accept_candidate(
345    candidate: &Name,
346    graph: &crate::implements_graph::ImplementsGraph,
347    fields_by_type_name: &HashMap<Name, IndexMap<String, FieldDef>>,
348    accepted: &mut IndexSet<Name>,
349    accumulated_signatures: &mut IndexMap<String, FieldDef>,
350    self_name: Option<&Name>,
351) {
352    let closure = graph.closure(candidate);
353
354    let would_cycle = self_name.is_some_and(|n| closure.contains(n));
355    let would_conflict = closure.iter().any(|name| {
356        fields_by_type_name
357            .get(name)
358            .into_iter()
359            .flatten()
360            .any(|(fname, fdef)| {
361                accumulated_signatures.get(fname).is_some_and(|existing| {
362                    existing.ty != fdef.ty
363                        || existing.arguments_definition != fdef.arguments_definition
364                })
365            })
366    });
367    if would_cycle || would_conflict {
368        return;
369    }
370
371    for name in closure {
372        if !accepted.insert(name.clone()) {
373            continue;
374        }
375        for (fname, fdef) in fields_by_type_name.get(&name).into_iter().flatten() {
376            accumulated_signatures
377                .entry(fname.clone())
378                .or_insert_with(|| fdef.clone());
379        }
380    }
381}
382
383/// Union of every def's fields by type name, merging base + extensions.
384/// First occurrence of each field name wins.
385pub(crate) fn fields_from_all_definitions<T: NamedDef>(
386    defs: &[T],
387) -> HashMap<Name, IndexMap<String, FieldDef>> {
388    unique_names(defs)
389        .into_iter()
390        .map(|n| {
391            let fields = field_signatures_for(defs, &n);
392            (n, fields)
393        })
394        .collect()
395}
396
397/// Distinct names across base + extensions, in first-occurrence order.
398pub(crate) fn unique_names<T: NamedDef>(defs: &[T]) -> Vec<Name> {
399    let mut seen: IndexSet<Name> = IndexSet::new();
400    for d in defs {
401        seen.insert(d.name().clone());
402    }
403    seen.into_iter().collect()
404}
405
406/// Index of the base def for `name` (`extend: false`), falling back to
407/// the first matching def when no base exists.
408pub(crate) fn base_def_index<T: NamedDef>(defs: &[T], name: &Name) -> Option<usize> {
409    defs.iter()
410        .position(|d| !d.is_extend() && d.name() == name)
411        .or_else(|| defs.iter().position(|d| d.name() == name))
412}
413
414/// Positions of every def (base + extensions) sharing the given name.
415pub(crate) fn def_indices_with_name<T: NamedDef>(defs: &[T], name: &Name) -> Vec<usize> {
416    defs.iter()
417        .enumerate()
418        .filter_map(|(i, d)| (d.name() == name).then_some(i))
419        .collect()
420}
421
422/// First-wins union of every field signature across `name`'s base +
423/// prior extensions. An extension picking new implements consults this
424/// so it doesn't accept a parent whose fields would overwrite anything
425/// the type already declares.
426pub(crate) fn field_signatures_for<T: NamedDef>(
427    defs: &[T],
428    name: &Name,
429) -> IndexMap<String, FieldDef> {
430    let mut out: IndexMap<String, FieldDef> = IndexMap::new();
431    for def in defs {
432        if def.name() == name {
433            for f in def.fields() {
434                out.entry(f.name.name.clone()).or_insert_with(|| f.clone());
435            }
436        }
437    }
438    out
439}
440
441/// Current (first-wins) union of every parent's fields, read live from
442/// `defs`. Used by the backfill so a child sees its parent's
443/// post-backfill signature rather than a stale snapshot.
444pub(crate) fn parent_fields_from_defs<T: NamedDef>(
445    parents: &IndexSet<Name>,
446    defs: &[T],
447) -> IndexMap<String, FieldDef> {
448    let mut out: IndexMap<String, FieldDef> = IndexMap::new();
449    for parent in parents {
450        for def in defs {
451            if def.name() == parent {
452                for f in def.fields() {
453                    out.entry(f.name.name.clone()).or_insert_with(|| f.clone());
454                }
455            }
456        }
457    }
458    out
459}
460
461impl StackedEntity for InterfaceTypeDef {
462    fn name(&self) -> &Name {
463        &self.name
464    }
465
466    fn fields_def(&self) -> &[FieldDef] {
467        &self.fields_def
468    }
469}