use proc_macro2::{Ident, TokenStream};
use quote::quote;
use crate::derive::data_models::{CStyleEnum, ConvertType, GodotConvert, NewtypeStruct, ViaType};
use crate::derive::derive_godot_convert::EnumeratorExprCache;
use crate::util;
pub fn make_fromgodot(convert: &GodotConvert, cache: &mut EnumeratorExprCache) -> TokenStream {
let GodotConvert {
ty_name: name,
convert_type: data,
} = convert;
match data {
ConvertType::NewType { field } => make_fromgodot_for_newtype_struct(name, field),
ConvertType::Enum {
variants,
via: ViaType::GString { .. },
} => make_fromgodot_for_gstring_enum(name, variants),
ConvertType::Enum {
variants,
via: ViaType::Int { int_ident },
} => make_fromgodot_for_int_enum(name, variants, int_ident, cache),
}
}
fn make_fromgodot_for_newtype_struct(name: &Ident, field: &NewtypeStruct) -> TokenStream {
let field_name = field.field_name();
let via_type = &field.ty;
quote! {
impl ::godot::meta::FromGodot for #name {
fn try_from_godot(via: #via_type) -> ::std::result::Result<Self, ::godot::meta::error::ConvertError> {
Ok(Self { #field_name: via })
}
}
}
}
fn make_fromgodot_for_int_enum(
name: &Ident,
enum_: &CStyleEnum,
int: &Ident,
cache: &mut EnumeratorExprCache,
) -> TokenStream {
let discriminants =
cache.map_ord_exprs(int, enum_.enumerator_names(), enum_.enumerator_ord_exprs());
let names = enum_.enumerator_names();
let bad_variant_error = format!("invalid {name} variant");
let ord_variables: Vec<Ident> = names
.iter()
.map(|e| util::ident(&format!("ORD_{e}")))
.collect();
quote! {
impl ::godot::meta::FromGodot for #name {
#[allow(unused_parens)] fn try_from_godot(via: #int) -> ::std::result::Result<Self, ::godot::meta::error::ConvertError> {
#(
#[allow(non_upper_case_globals)]
const #ord_variables: #int = #discriminants;
)*
match via {
#(
#ord_variables => Ok(#name::#names),
)*
other => Err(::godot::meta::error::ConvertError::with_error_value(#bad_variant_error, via))
}
}
}
}
}
fn make_fromgodot_for_gstring_enum(name: &Ident, enum_: &CStyleEnum) -> TokenStream {
let names = enum_.enumerator_names();
let names_str = names.iter().map(ToString::to_string).collect::<Vec<_>>();
let bad_variant_error = format!("invalid {name} variant");
quote! {
impl ::godot::meta::FromGodot for #name {
fn try_from_godot(via: ::godot::builtin::GString) -> ::std::result::Result<Self, ::godot::meta::error::ConvertError> {
match via.to_string().as_str() {
#(
#names_str => Ok(#name::#names),
)*
other => Err(::godot::meta::error::ConvertError::with_error_value(#bad_variant_error, via))
}
}
}
}
}