use super::{EnumVariant, Schema, TypeEnum};
pub struct TypesDiff<'a> {
items: Vec<TypesDiffItem<'a>>,
}
pub enum TypesDiffItem<'a> {
CreateType(&'a TypeEnum),
AddVariants {
ty: &'a TypeEnum,
added: Vec<&'a EnumVariant>,
},
}
impl<'a> TypesDiff<'a> {
pub fn from(previous: &'a Schema, next: &'a Schema) -> Self {
let prev_types = collect_named_enums(previous);
let next_types = collect_named_enums(next);
let mut items = Vec::new();
for (name, next_ty) in &next_types {
match prev_types.get(name) {
None => {
items.push(TypesDiffItem::CreateType(next_ty));
}
Some(prev_ty) => {
let prev_names: Vec<&str> =
prev_ty.variants.iter().map(|v| v.name.as_str()).collect();
let next_names: Vec<&str> =
next_ty.variants.iter().map(|v| v.name.as_str()).collect();
assert!(
next_names.len() >= prev_names.len(),
"enum type `{name}`: removing variants is not supported; \
previous had {} variants, next has {}",
prev_names.len(),
next_names.len()
);
for (i, prev_name) in prev_names.iter().enumerate() {
assert!(
next_names[i] == *prev_name,
"enum type `{name}`: variant at position {i} changed from \
`{prev_name}` to `{}`; reordering or renaming variants \
is not supported",
next_names[i]
);
}
if next_names.len() > prev_names.len() {
let added: Vec<&'a EnumVariant> =
next_ty.variants[prev_names.len()..].iter().collect();
items.push(TypesDiffItem::AddVariants { ty: next_ty, added });
}
}
}
}
Self { items }
}
pub fn iter(&self) -> impl Iterator<Item = &TypesDiffItem<'a>> {
self.items.iter()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
}
fn collect_named_enums(schema: &Schema) -> hashbrown::HashMap<&str, &TypeEnum> {
let mut result = hashbrown::HashMap::new();
for table in &schema.tables {
for column in &table.columns {
if let super::Type::Enum(type_enum) = &column.storage_ty
&& let Some(name) = &type_enum.name
{
result.entry(name.as_str()).or_insert(type_enum);
}
}
}
result
}