use crate::api_model::{ApiModel, FunctionModel, ObjectModel};
use crate::emitter::core::*;
pub(crate) fn emit_object(
o: &ObjectModel,
constructor: Option<&FunctionModel>,
model: &ApiModel,
index: &TypeIndex,
c_prefix: &str,
) -> String {
let name = type_name(&o.name);
let mut methods = Vec::new();
let send_sync_impl = if matches!(name.as_str(), "Instance" | "Adapter") {
String::new()
} else {
format!(
r#"unsafe impl Send for {name} {{}}
unsafe impl Sync for {name} {{}}
"#,
name = name
)
};
if let Some(func) = constructor {
let signature = fn_signature_params(&func.def.args, model, None);
let (arg_prelude, ffi_args, has_callback) =
emit_ffi_arg_prelude(&func.def.args, model, index, c_prefix);
let func_name = ffi_fn_name(&func.name, c_prefix);
let args = ffi_args.join(", ");
let ffi_call = format!(
" let result = unsafe {{ ffi::{func}({args}) }};",
func = func_name,
args = args
);
let ret = emit_return_conversion(func.def.returns(), index, "result");
let postlude = emit_out_struct_postlude(&func.def.args, index);
let body = if postlude.is_empty() {
format!("{}\n{}", ffi_call, ret)
} else {
format!("{}\n{}\n{}", ffi_call, postlude, ret)
};
methods.push(format!(
r#" pub fn new({signature}) -> Self {{
{arg_prelude}
{body}
}}"#,
signature = signature,
arg_prelude = indent_block(&arg_prelude, 8),
body = if has_callback {
" unimplemented!()".to_string()
} else {
body
}
));
}
for method in &o.def.methods {
let method_name = safe_ident(&snake_case_name(&method.name));
let return_ty = method
.returns()
.map(|ret| rust_return_type(ret))
.unwrap_or_else(|| "()".to_string());
let signature = fn_signature_params(&method.args, model, Some("self"));
let (arg_prelude, ffi_args, has_callback) =
emit_ffi_arg_prelude(&method.args, model, index, c_prefix);
let postlude = emit_out_struct_postlude(&method.args, index);
methods.push(format!(
r#" pub fn {method_name}({signature}) -> {return_ty} {{
{arg_prelude}
{body}
}}"#,
method_name = method_name,
signature = signature,
return_ty = return_ty,
arg_prelude = indent_block(&arg_prelude, 8),
body = if has_callback {
" unimplemented!()".to_string()
} else {
let args = if ffi_args.is_empty() {
"".to_string()
} else {
format!(", {}", ffi_args.join(", "))
};
if method
.returns()
.map(|ret| ret.get_type() == "void")
.unwrap_or(true)
{
let func_name = ffi_fn_name(&format!("{} {}", o.name, method.name), c_prefix);
let postlude = if postlude.is_empty() {
String::new()
} else {
format!("\n{postlude}", postlude = postlude)
};
format!(
" unsafe {{ ffi::{func}(self.raw{args}) }};{postlude}\n ()",
func = func_name,
args = args,
postlude = postlude
)
} else {
let func_name = ffi_fn_name(&format!("{} {}", o.name, method.name), c_prefix);
let ffi_call = format!(
" let result = unsafe {{ ffi::{func}(self.raw{args}) }};",
func = func_name,
args = args
);
let ret = emit_return_conversion(method.returns(), index, "result");
if postlude.is_empty() {
format!("{}\n{}", ffi_call, ret)
} else {
format!("{}\n{}\n{}", ffi_call, postlude, ret)
}
}
}
));
}
let methods_block = methods.join("\n\n");
format!(
r#"#[derive(Debug)]
pub struct {name} {{
raw: ffi::{prefix}{name},
}}
impl {name} {{
pub(crate) unsafe fn from_raw(raw: ffi::{prefix}{name}) -> Self {{
Self {{ raw }}
}}
pub(crate) fn as_raw(&self) -> ffi::{prefix}{name} {{
self.raw
}}
{methods}
}}
impl Drop for {name} {{
fn drop(&mut self) {{
if self.as_raw().is_null() {{
return;
}}
unsafe {{ ffi::wgpu{name}Release(self.raw) }};
}}
}}
impl Clone for {name} {{
fn clone(&self) -> Self {{
unsafe {{ ffi::wgpu{name}AddRef(self.raw) }};
Self {{ raw: self.raw }}
}}
}}
{send_sync_impl}"#,
name = name,
methods = methods_block,
prefix = c_prefix,
send_sync_impl = send_sync_impl
)
}