use proc_macro2::{Ident, TokenStream};
use quote::{ToTokens, format_ident, quote};
use crate::context::Context;
use crate::conv;
use crate::generator::{enums, gdext_build_struct};
use crate::models::domain::{ExtensionApi, FlowDirection};
use crate::util::ident;
pub fn make_sys_central_code(api: &ExtensionApi) -> TokenStream {
let build_config_struct = gdext_build_struct::make_gdext_build_struct(&api.godot_version);
let variant_type_enum = make_variant_type_enum(api, true);
let [opaque_32bit, opaque_64bit] = make_opaque_types(api);
let godot_type_name_method = make_godot_type_name_method(api);
quote! {
#[cfg(target_pointer_width = "32")] #[cfg_attr(published_docs, doc(cfg(target_pointer_width = "32")))]
pub mod types {
#(#opaque_32bit)*
}
#[cfg(target_pointer_width = "64")] #[cfg_attr(published_docs, doc(cfg(target_pointer_width = "64")))]
pub mod types {
#(#opaque_64bit)*
}
#build_config_struct
#variant_type_enum
impl VariantType {
#[doc(hidden)]
pub fn from_sys(enumerator: crate::GDExtensionVariantType) -> Self {
#[allow(clippy::unnecessary_cast)] Self { ord: enumerator as i32 }
}
#[doc(hidden)]
pub fn sys(self) -> crate::GDExtensionVariantType {
self.ord as _
}
#godot_type_name_method
}
}
}
pub fn make_core_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStream {
let VariantEnums {
variant_ty_enumerators_pascal,
variant_ty_enumerators_shout,
variant_ty_enumerators_rust,
..
} = make_variant_enums(api, ctx);
let (global_enum_defs, global_reexported_enum_defs) = make_global_enums(api);
let variant_type_traits = make_variant_type_enum(api, false);
quote! {
use crate::builtin::*;
use crate::classes::Object;
use crate::obj::Gd;
#variant_type_traits
#[allow(dead_code)]
pub enum VariantDispatch {
Nil,
#(
#variant_ty_enumerators_pascal(#variant_ty_enumerators_rust),
)*
FreedObject,
}
impl VariantDispatch {
pub fn from_variant(variant: &Variant) -> Self {
match variant.get_type() {
VariantType::NIL => Self::Nil,
VariantType::OBJECT if !variant.is_object_alive() => Self::FreedObject,
#(
VariantType::#variant_ty_enumerators_shout
=> Self::#variant_ty_enumerators_pascal(variant.to::<#variant_ty_enumerators_rust>()),
)*
_ => panic!("Variant type not supported: {:?}", variant.get_type()),
}
}
}
impl std::fmt::Debug for VariantDispatch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Nil => write!(f, "null"),
#(
Self::#variant_ty_enumerators_pascal(v) => write!(f, "{v:?}"),
)*
Self::FreedObject => write!(f, "<Freed Object>"),
}
}
}
pub mod global_enums {
use crate::sys;
#( #global_enum_defs )*
}
pub mod global_reexported_enums {
use crate::sys;
#( #global_reexported_enum_defs )*
}
}
}
struct VariantEnums {
variant_ty_enumerators_pascal: Vec<Ident>,
variant_ty_enumerators_shout: Vec<Ident>,
variant_ty_enumerators_rust: Vec<TokenStream>,
}
fn make_opaque_types(api: &ExtensionApi) -> [Vec<TokenStream>; 2] {
let mut opaque_types = [Vec::new(), Vec::new()];
for b in api.builtin_sizes.iter() {
let index = b.config.is_64bit() as usize;
let type_def = make_opaque_type(&b.builtin_original_name, b.size);
opaque_types[index].push(type_def);
}
opaque_types
}
fn make_opaque_type(godot_original_name: &str, size: usize) -> TokenStream {
let name = conv::to_pascal_case(godot_original_name);
let (first, rest) = name.split_at(1);
let ident = format_ident!("Opaque{}{}", first.to_ascii_uppercase(), rest);
quote! {
pub type #ident = crate::opaque::Opaque<#size>;
}
}
fn make_variant_enums(api: &ExtensionApi, ctx: &mut Context) -> VariantEnums {
let len = api.builtins.len();
let mut result = VariantEnums {
variant_ty_enumerators_pascal: Vec::with_capacity(len),
variant_ty_enumerators_shout: Vec::with_capacity(len),
variant_ty_enumerators_rust: Vec::with_capacity(len),
};
let flow = FlowDirection::GodotToRust;
for builtin in api.builtins.iter() {
let original_name = builtin.godot_original_name();
let shout_case = builtin.godot_shout_name();
let rust_ty = conv::to_rust_type(original_name, None, Some(flow), ctx);
let pascal_case = conv::to_pascal_case(original_name);
result
.variant_ty_enumerators_pascal
.push(ident(&pascal_case));
result.variant_ty_enumerators_shout.push(ident(shout_case));
result
.variant_ty_enumerators_rust
.push(rust_ty.to_token_stream());
}
result
}
fn make_global_enums(api: &ExtensionApi) -> (Vec<TokenStream>, Vec<TokenStream>) {
let mut global_enum_defs = vec![];
let mut global_reexported_enum_defs = vec![];
for enum_ in api.global_enums.iter() {
if enum_.name == "VariantType" {
continue;
}
let def = enums::make_enum_definition(enum_);
if enum_.is_private {
global_reexported_enum_defs.push(def);
} else {
global_enum_defs.push(def);
}
}
(global_enum_defs, global_reexported_enum_defs)
}
fn make_variant_type_enum(api: &ExtensionApi, is_definition: bool) -> TokenStream {
let variant_type_enum = api
.global_enums
.iter()
.find(|e| e.name == "VariantType")
.expect("missing VariantType enum in API JSON");
let define_enum = is_definition;
let define_traits = !is_definition;
enums::make_enum_definition_with(variant_type_enum, define_enum, define_traits)
}
fn make_godot_type_name_method(api: &ExtensionApi) -> TokenStream {
let mut ordinals = vec![0i32];
let mut names: Vec<&str> = vec!["Variant"];
for builtin in api.builtins.iter() {
ordinals.push(builtin.variant_type_ord);
names.push(builtin.godot_original_name());
}
quote! {
pub fn godot_type_name(&self) -> &'static str {
match self.ord {
#( #ordinals => #names, )*
_ => "Unknown",
}
}
}
}