use crate::util::get_single_tuple_variant;
use crate::util::index_to_u8;
use proc_macro::TokenStream;
use proc_macro_error::abort;
use syn::parse_macro_input;
use syn::Item;
use syn::ItemEnum;
pub fn build(_attrs: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as Item);
let (ident, variants) = match &input {
Item::Enum(ItemEnum {
ident, variants, ..
}) => (ident, variants),
_ => abort!(input, "hdk_dependent_entry_types can only be used on Enums"),
};
let key_to_variant: proc_macro2::TokenStream = variants
.iter()
.enumerate()
.map(|(i, v)| (index_to_u8(i), v))
.map(
|(
i,
syn::Variant {
ident: v_ident,
fields,
..
},
)| {
let ty = &get_single_tuple_variant(v_ident, fields).ty;
quote::quote! {
ZomeTypesKey {
zome_index: ZomeDependencyIndex(#i),
type_index,
} => {
let key = ZomeTypesKey {
zome_index: 0.into(),
type_index,
};
<#ty as UnitEnum>::Unit::iter()
.find_map(|unit| (ZomeEntryTypesKey::from(unit) == key).then(|| unit))
.map_or(Ok(None), |unit| Ok(Some(Self::#v_ident(#ty::try_from((unit, entry))?))))
}
}
},
)
.collect();
let try_into_entry: proc_macro2::TokenStream = variants
.into_iter()
.map(
|syn::Variant {
ident: v_ident,
fields,
..
}| {
get_single_tuple_variant(v_ident, fields);
quote::quote! {#ident::#v_ident (v) => Entry::try_from(v),}
},
)
.collect();
let into_visibility: proc_macro2::TokenStream = variants
.into_iter()
.map(
|syn::Variant {
ident: v_ident,
fields,
..
}| {
get_single_tuple_variant(v_ident, fields);
quote::quote! {#ident::#v_ident (v) => EntryVisibility::from(v),}
},
)
.collect();
let output = quote::quote! {
#[hdk_to_coordinates(nested = true, entry = true)]
#[derive(Debug)]
#input
impl TryFrom<&#ident> for Entry {
type Error = WasmError;
fn try_from(value: &#ident) -> Result<Self, Self::Error> {
match value {
#try_into_entry
}
}
}
impl TryFrom<#ident> for Entry {
type Error = WasmError;
fn try_from(value: #ident) -> Result<Self, Self::Error> {
Entry::try_from(&value)
}
}
impl TryFrom<&#ident> for ScopedEntryDefIndex {
type Error = WasmError;
fn try_from(value: &#ident) -> Result<Self, Self::Error> {
match zome_info()?.zome_types.entries.get(value) {
Some(t) => Ok(t),
_ => Err(wasm_error!(WasmErrorInner::Guest(format!(
"{:?} does not map to any ZomeIndex and EntryDefIndex that is in scope for this zome. Make sure you have all zome dependencies specified correctly in the dna manifest.",
value
)))),
}
}
}
impl TryFrom<#ident> for ScopedEntryDefIndex {
type Error = WasmError;
fn try_from(value: #ident) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl TryFrom<&&#ident> for ScopedEntryDefIndex {
type Error = WasmError;
fn try_from(value: &&#ident) -> Result<Self, Self::Error> {
Self::try_from(*value)
}
}
impl From<&#ident> for EntryVisibility {
fn from(v: &#ident) -> Self {
match v {
#into_visibility
}
}
}
impl From<&&#ident> for EntryVisibility {
fn from(v: &&#ident) -> Self {
Self::from(*v)
}
}
impl EntryTypesHelper for #ident {
type Error = WasmError;
fn deserialize_from_type<Z, I>(
zome_index: Z,
entry_def_index: I,
entry: &Entry,
) -> Result<Option<Self>, Self::Error>
where
Z: Into<ZomeIndex>,
I: Into<EntryDefIndex>
{
let scoped_type = ScopedEntryDefIndex {
zome_index: zome_index.into(),
zome_type: entry_def_index.into(),
};
let entries = zome_info()?.zome_types.entries;
match entries.find_key(scoped_type) {
Some(key) => {
match key {
#key_to_variant
_ => Ok(None),
}
}
None => if entries.dependencies().any(|z| z == scoped_type.zome_index) {
Err(wasm_error!(WasmErrorInner::Guest(format!(
"Entry type: {:?} is out of range for this zome.",
scoped_type
))))
} else {
Ok(None)
}
}
}
}
};
output.into()
}