use std::collections::{HashMap, HashSet};
use move_syn::{FieldsKind, ItemPath};
use quote::quote;
use unsynn::{Ident, ToTokens as _, TokenStream};
use crate::generics::GenericsExt;
use crate::iter::BoxedIter as _;
use crate::{ItemContext, Result, named_fields, positional_fields};
pub(super) fn to_rust(
this: &move_syn::Enum,
otw_types: HashSet<Ident>,
ctx: ItemContext,
) -> Result<TokenStream> {
let move_syn::Enum {
ident, generics, ..
} = this;
let extra_attrs: TokenStream = ctx
.package
.into_iter()
.map(unsynn::ToTokens::to_token_stream)
.map(|addr| quote!(#[move_(address = #addr)]))
.chain(ctx.module.map(|ident| quote!(#[move_(module = #ident)])))
.collect();
let type_generics = generics
.as_ref()
.map(|g| g.type_generics(ctx.thecrate, otw_types))
.transpose()
.map(Option::unwrap_or_default)?;
let mut phantoms = unused_phantoms(this);
let variants = this
.variants()
.map(|var| variant_to_rust(var, &std::mem::take(&mut phantoms), ctx.address_map));
let thecrate = ctx.thecrate;
let serde_crate = format!("{thecrate}::serde").replace(" ", "");
Ok(quote! {
#[derive(
Clone,
Debug,
PartialEq,
Eq,
Hash,
#thecrate::traits::MoveDatatype,
#thecrate::serde::Deserialize,
#thecrate::serde::Serialize,
)]
#[move_(crate = #thecrate::traits)]
#[serde(crate = #serde_crate)]
#extra_attrs
#[allow(non_snake_case)]
pub enum #ident #type_generics {
#(#variants),*
}
})
}
fn unused_phantoms(this: &move_syn::Enum) -> Vec<Ident> {
let Some(generics) = this.generics.as_ref() else {
return Vec::new(); };
let maybe_phantom_leaf_types: HashSet<_> = enum_leaf_types(this)
.filter_map(|path| match path {
ItemPath::Ident(ident) => Some(ident),
_ => None,
})
.collect();
generics
.phantoms()
.filter(|&ident| !maybe_phantom_leaf_types.contains(ident))
.cloned()
.collect()
}
fn enum_leaf_types(this: &move_syn::Enum) -> Box<dyn Iterator<Item = &ItemPath> + '_> {
this.variants()
.flat_map(|var| &var.fields)
.flat_map(|fields| match fields {
FieldsKind::Positional(positional) => {
leaf_types_recursive(positional.fields().map(|field| &field.ty).boxed())
}
FieldsKind::Named(named) => {
leaf_types_recursive(named.fields().map(|field| &field.ty).boxed())
}
})
.boxed()
}
fn leaf_types_recursive<'a>(
types: Box<dyn Iterator<Item = &'a move_syn::Type> + 'a>,
) -> Box<dyn Iterator<Item = &'a ItemPath> + 'a> {
types
.into_iter()
.flat_map(|t| {
t.type_args.as_ref().map_or_else(
|| std::iter::once(&t.path).boxed(),
|t_args| leaf_types_recursive(t_args.types().boxed()),
)
})
.boxed()
}
fn variant_to_rust(
this: &move_syn::EnumVariant,
phantoms: &[Ident],
address_map: &HashMap<Ident, TokenStream>,
) -> TokenStream {
use move_syn::FieldsKind as K;
let move_syn::EnumVariant {
attrs,
ident,
fields,
} = this;
let attrs = attrs
.iter()
.filter(|attr| attr.is_doc())
.map(|attr| attr.to_token_stream());
let bool_if_empty = false;
let visibility = false;
let default_fields = (!phantoms.is_empty()).then(|| {
positional_fields::to_rust(
&Default::default(),
phantoms.iter(),
address_map,
bool_if_empty,
visibility,
)
});
let fields = fields
.as_ref()
.map(|kind| match kind {
K::Named(named) => {
named_fields::to_rust(named, phantoms.iter(), address_map, visibility)
}
K::Positional(positional) => positional_fields::to_rust(
positional,
phantoms.iter(),
address_map,
bool_if_empty,
visibility,
),
})
.or(default_fields)
.unwrap_or_default();
quote! {
#(#attrs)*
#ident #fields
}
}