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(¤t);
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)
}