fn diff()

in compiler/crates/schema-diff/src/lib.rs [290:534]


fn diff(current: Vec<TypeSystemDefinition>, previous: Vec<TypeSystemDefinition>) -> SchemaChange {
    let mut changes = vec![];
    let mut current_map = build_current_map(&current);

    for definition in previous {
        match definition {
            TypeSystemDefinition::EnumTypeDefinition(EnumTypeDefinition {
                name,
                values: previous_values,
                ..
            }) => {
                let def = current_map.remove(&name.value);
                match (def, previous_values) {
                    (
                        Some(TypeSystemDefinition::EnumTypeDefinition(EnumTypeDefinition {
                            values: Some(values),
                            ..
                        })),
                        Some(previous_values),
                    ) => {
                        if values.items.len() != previous_values.items.len() {
                            changes.push(DefinitionChange::EnumChanged { name: name.value });
                        } else {
                            for i in 0..values.items.len() {
                                if values.items[i].name.value != previous_values.items[i].name.value
                                {
                                    changes
                                        .push(DefinitionChange::EnumChanged { name: name.value });
                                    break;
                                }
                            }
                        }
                    }
                    (None, _) => {
                        changes.push(DefinitionChange::EnumRemoved(name.value));
                    }
                    (Some(def), _) => {
                        if !add_definition(&mut changes, def) {
                            return SchemaChange::GenericChange;
                        }
                        changes.push(DefinitionChange::EnumRemoved(name.value));
                    }
                }
            }

            TypeSystemDefinition::UnionTypeDefinition(UnionTypeDefinition {
                name,
                members: previous_members,
                ..
            }) => {
                let def = current_map.remove(&name.value);
                match def {
                    Some(TypeSystemDefinition::UnionTypeDefinition(UnionTypeDefinition {
                        members,
                        ..
                    })) => {
                        let (added, removed) = compare_string_keys(members, previous_members);
                        if !added.is_empty() || !removed.is_empty() {
                            changes.push(DefinitionChange::UnionChanged {
                                name: name.value,
                                added,
                                removed,
                            });
                        }
                    }
                    None => {
                        changes.push(DefinitionChange::UnionRemoved(name.value));
                    }
                    Some(def) => {
                        if !add_definition(&mut changes, def) {
                            return SchemaChange::GenericChange;
                        }
                        changes.push(DefinitionChange::UnionRemoved(name.value));
                    }
                }
            }

            TypeSystemDefinition::InputObjectTypeDefinition(InputObjectTypeDefinition {
                name,
                fields: previous_fields,
                ..
            }) => {
                let def = current_map.remove(&name.value);
                match def {
                    Some(TypeSystemDefinition::InputObjectTypeDefinition(
                        InputObjectTypeDefinition {
                            fields: Some(fields),
                            ..
                        },
                    )) => {
                        let (added, removed) = compare_input_value_definition(
                            &fields.items,
                            previous_fields
                                .into_iter()
                                .flat_map(|list| list.items)
                                .collect(),
                        );
                        if !added.is_empty() || !removed.is_empty() {
                            changes.push(DefinitionChange::InputObjectChanged {
                                name: name.value,
                                added,
                                removed,
                            });
                        }
                    }
                    None
                    | Some(TypeSystemDefinition::InputObjectTypeDefinition(
                        InputObjectTypeDefinition { fields: None, .. },
                    )) => {
                        changes.push(DefinitionChange::InputObjectRemoved(name.value));
                    }
                    Some(def) => {
                        if !add_definition(&mut changes, def) {
                            return SchemaChange::GenericChange;
                        }
                        changes.push(DefinitionChange::InputObjectRemoved(name.value));
                    }
                }
            }

            TypeSystemDefinition::InterfaceTypeDefinition(InterfaceTypeDefinition {
                name,
                fields: optional_previous_fields,
                ..
            }) => {
                let def = current_map.remove(&name.value);
                match def {
                    Some(TypeSystemDefinition::InterfaceTypeDefinition(
                        InterfaceTypeDefinition {
                            fields: optional_current_fields,
                            ..
                        },
                    )) => {
                        let (added, removed, changed) =
                            compare_fields(optional_current_fields, optional_previous_fields);
                        if !added.is_empty() || !removed.is_empty() || !changed.is_empty() {
                            changes.push(DefinitionChange::InterfaceChanged {
                                name: name.value,
                                added,
                                removed,
                                changed,
                            });
                        }
                    }
                    None => {
                        changes.push(DefinitionChange::InterfaceRemoved(name.value));
                    }
                    Some(def) => {
                        if !add_definition(&mut changes, def) {
                            return SchemaChange::GenericChange;
                        }
                        changes.push(DefinitionChange::InterfaceRemoved(name.value));
                    }
                }
            }

            TypeSystemDefinition::ObjectTypeDefinition(ObjectTypeDefinition {
                name,
                interfaces: previous_interfaces,
                fields: optional_previous_fields,
                ..
            }) => {
                let def = current_map.remove(&name.value);
                match def {
                    Some(TypeSystemDefinition::ObjectTypeDefinition(ObjectTypeDefinition {
                        interfaces: current_interfaces,
                        fields: optional_current_fields,
                        ..
                    })) => {
                        let (added, removed, changed) =
                            compare_fields(optional_current_fields, optional_previous_fields);
                        let (interfaces_added, interfaces_removed) =
                            compare_string_keys(current_interfaces, previous_interfaces);
                        if !added.is_empty()
                            || !removed.is_empty()
                            || !changed.is_empty()
                            || !interfaces_added.is_empty()
                            || !interfaces_removed.is_empty()
                        {
                            changes.push(DefinitionChange::ObjectChanged {
                                name: name.value,
                                added,
                                removed,
                                changed,
                                interfaces_added,
                                interfaces_removed,
                            });
                        }
                    }
                    None => {
                        changes.push(DefinitionChange::ObjectRemoved(name.value));
                    }
                    Some(def) => {
                        if !add_definition(&mut changes, def) {
                            return SchemaChange::GenericChange;
                        }
                        changes.push(DefinitionChange::ObjectRemoved(name.value));
                    }
                }
            }

            TypeSystemDefinition::ScalarTypeDefinition(ScalarTypeDefinition { name, .. }) => {
                let def = current_map.remove(&name.value);
                match def {
                    None => {
                        changes.push(DefinitionChange::ScalarRemoved(name.value));
                    }
                    Some(TypeSystemDefinition::ScalarTypeDefinition(ScalarTypeDefinition {
                        ..
                    })) => {}
                    Some(def) => {
                        if !add_definition(&mut changes, def) {
                            return SchemaChange::GenericChange;
                        }
                        changes.push(DefinitionChange::ScalarRemoved(name.value));
                    }
                }
            }

            // We skip diffing the following definitions
            TypeSystemDefinition::InterfaceTypeExtension(..)
            | TypeSystemDefinition::ObjectTypeExtension(..)
            | TypeSystemDefinition::SchemaDefinition(..)
            | TypeSystemDefinition::DirectiveDefinition(..)
            | TypeSystemDefinition::SchemaExtension(_)
            | TypeSystemDefinition::EnumTypeExtension(_)
            | TypeSystemDefinition::UnionTypeExtension(_)
            | TypeSystemDefinition::InputObjectTypeExtension(_)
            | TypeSystemDefinition::ScalarTypeExtension(_) => {}
        }
    }

    for (_, definition) in current_map.drain().into_iter() {
        add_definition(&mut changes, definition);
    }

    if changes.is_empty() {
        // The schema has changed, but we currently don't detect directive definition changes,
        // schema definition changes, and we don't parse client extensions.
        // But we can add them later if some of the changes don't require full rebuilds.
        return SchemaChange::GenericChange;
    }

    SchemaChange::DefinitionChanges(changes)
}