Type-safe signals by Bromeon · Pull Request #1000 · godot-rust/gdext (original) (raw)

impl<R: ToGodot + FromGodot + Debug, Ps: InParams + OutParams> VarcallSignatureTuple for SignatureThing<R, Ps> { const PARAM_COUNT: usize = ::PARAM_LEN;

fn param_property_info(index: usize, param_name: &str) -> PropertyInfo {
    todo!()
}

fn param_info(index: usize, param_name: &str) -> Option<MethodParamOrReturnInfo> {
    todo!()
}

fn return_info() -> Option<MethodParamOrReturnInfo> {
    todo!()
}

unsafe fn in_varcall(
    instance_ptr: godot_ffi::GDExtensionClassInstancePtr,
    call_ctx: &CallContext,
    args_ptr: *const godot_ffi::GDExtensionConstVariantPtr,
    arg_count: i64,
    ret: godot_ffi::GDExtensionVariantPtr,
    err: *mut godot_ffi::GDExtensionCallError,
    func: fn(godot_ffi::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret,
) -> Result<(), CallError> {
    //$crate::out!("in_varcall: {call_ctx}");
    CallError::check_arg_count(
        call_ctx,
        arg_count as usize,
        <Self::Params as Params>::PARAM_LEN,
    )?;

    #[cfg(feature = "trace")]
    trace::push(true, false, &call_ctx);

    let args = unsafe { <Self::Params as InParams>::varcall_args(args_ptr, call_ctx)? };

    let rust_result = func(instance_ptr, args);
    varcall_return::<R>(rust_result, ret, err);
    Ok(())
}

unsafe fn out_class_varcall(
    method_bind: ClassMethodBind,
    // Separate parameters to reduce tokens in generated class API.
    class_name: &'static str,
    method_name: &'static str,
    object_ptr: godot_ffi::GDExtensionObjectPtr,
    maybe_instance_id: Option<InstanceId>, // if not static
    args: Self::Params,
    varargs: &[Variant],
) -> Result<Self::Ret, CallError> {
    let call_ctx = CallContext::outbound(class_name, method_name);
    //$crate::out!("out_class_varcall: {call_ctx}");

    // Note: varcalls are not safe from failing, if they happen through an object pointer -> validity check necessary.
    if let Some(instance_id) = maybe_instance_id {
        crate::classes::ensure_object_alive(instance_id, object_ptr, &call_ctx);
    }

    let class_fn = sys::interface_fn!(object_method_bind_call);

    let variant: Result<Variant, CallError> = args.with_variant_array(|explicit_args| {
        let mut variant_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len());
        variant_ptrs.extend(explicit_args.iter().map(Variant::var_sys));
        variant_ptrs.extend(varargs.iter().map(Variant::var_sys));

        Variant::new_with_var_uninit_result(|return_ptr| {
            let mut err = sys::default_call_error();
            class_fn(
                method_bind.0,
                object_ptr,
                variant_ptrs.as_ptr(),
                variant_ptrs.len() as i64,
                return_ptr,
                std::ptr::addr_of_mut!(err),
            );

            sys::CallError::try_from_sys(err).map_err(|err| {
                CallError::check_out_varcall(&call_ctx, err, &explicit_args, varargs)
            })
        })
    });

    variant.and_then(|v| {
        v.try_to::<Self::Ret>()
            .map_err(|e| CallError::failed_return_conversion::<Self::Ret>(&call_ctx, e))
    })
}

#[cfg(since_api = "4.3")]
unsafe fn out_script_virtual_call(
    // Separate parameters to reduce tokens in macro-generated API.
    class_name: &'static str,
    method_name: &'static str,
    method_sname_ptr: godot_ffi::GDExtensionConstStringNamePtr,
    object_ptr: godot_ffi::GDExtensionObjectPtr,
    args: Self::Params,
) -> Self::Ret {
    // Assumes that caller has previously checked existence of a virtual method.

    let call_ctx = CallContext::outbound(class_name, method_name);
    //$crate::out!("out_script_virtual_call: {call_ctx}");

    let object_call_script_method = sys::interface_fn!(object_call_script_method);
    let variant = args.with_variant_array(|explicit_args| {
        let variant_ptrs = explicit_args
            .iter()
            .map(Variant::var_sys)
            .collect::<Vec<_>>();

        Variant::new_with_var_uninit(|return_ptr| {
            let mut err = sys::default_call_error();
            object_call_script_method(
                object_ptr,
                method_sname_ptr,
                variant_ptrs.as_ptr(),
                variant_ptrs.len() as i64,
                return_ptr,
                std::ptr::addr_of_mut!(err),
            );
        })
    });

    let result = <Self::Ret as FromGodot>::try_from_variant(&variant);
    result.unwrap_or_else(|err| return_error::<Self::Ret>(&call_ctx, err))
}

unsafe fn out_utility_ptrcall_varargs(
    utility_fn: UtilityFunctionBind,
    function_name: &'static str,
    args: Self::Params,
    varargs: &[Variant],
) -> Self::Ret {
    let call_ctx = CallContext::outbound("", function_name);
    //$crate::out!("out_utility_ptrcall_varargs: {call_ctx}");

    let result = args.with_ptr_array(|explicit_args| {
        let mut type_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len());
        type_ptrs.extend(explicit_args);
        type_ptrs.extend(varargs.iter().map(sys::GodotFfi::sys));

        // Important: this calls from_sys_init_default().
        new_from_ptrcall::<Self::Ret>(|return_ptr| {
            utility_fn(return_ptr, type_ptrs.as_ptr(), type_ptrs.len() as i32);
        })
    });

    result.unwrap_or_else(|err| return_error::<Self::Ret>(&call_ctx, err))
}

unsafe fn out_builtin_ptrcall_varargs(
    builtin_fn: BuiltinMethodBind,
    class_name: &'static str,
    method_name: &'static str,
    type_ptr: godot_ffi::GDExtensionTypePtr,
    args: Self::Params,
    varargs: &[Variant],
) -> Self::Ret {
    let call_ctx = CallContext::outbound(class_name, method_name);
    //$crate::out!("out_builtin_ptrcall_varargs: {call_ctx}");
    let result = args.with_ptr_array(|explicit_args| {
        let mut type_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len());
        type_ptrs.extend(explicit_args);
        type_ptrs.extend(varargs.iter().map(sys::GodotFfi::sys));

        // Important: this calls from_sys_init_default().
        new_from_ptrcall::<Self::Ret>(|return_ptr| {
            builtin_fn(
                type_ptr,
                type_ptrs.as_ptr(),
                return_ptr,
                type_ptrs.len() as i32,
            );
        })
    });

    result.unwrap_or_else(|err| return_error::<Self::Ret>(&call_ctx, err))
}

fn format_args(args: &Self::Params) -> String {
    todo!()
}

}