Skip to main content

dawn_codegen/
emitter.rs

1use crate::api_model::{
2    ApiModel, BitmaskModel, CallbackFunctionModel, CallbackInfoModel, CallbackModel, ConstantModel,
3    EnumModel, FunctionModel, ObjectModel, StructureModel,
4};
5use crate::parser::{EnumValueDef, LengthValue, RecordMember, ReturnType};
6use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
7use serde_json::Value;
8use std::collections::{HashMap, HashSet};
9use std::fs;
10use std::path::Path;
11
12pub struct GeneratedFiles {
13    pub enums: String,
14    pub structs: String,
15    pub extensions: String,
16    pub objects: String,
17    pub callbacks: String,
18    pub functions: String,
19    pub constants: String,
20    pub mod_rs: String,
21}
22
23impl GeneratedFiles {
24    pub fn write_to_dir(&self, out_dir: &Path) -> std::io::Result<()> {
25        fs::create_dir_all(out_dir)?;
26        fs::write(out_dir.join("enums.rs"), &self.enums)?;
27        fs::write(out_dir.join("structs.rs"), &self.structs)?;
28        fs::write(out_dir.join("extensions.rs"), &self.extensions)?;
29        fs::write(out_dir.join("objects.rs"), &self.objects)?;
30        fs::write(out_dir.join("callbacks.rs"), &self.callbacks)?;
31        fs::write(out_dir.join("functions.rs"), &self.functions)?;
32        fs::write(out_dir.join("constants.rs"), &self.constants)?;
33        fs::write(out_dir.join("mod.rs"), &self.mod_rs)?;
34        Ok(())
35    }
36}
37
38pub fn generate_strings(model: &ApiModel) -> GeneratedFiles {
39    generate_strings_with_ffi_consts(model, None)
40}
41
42pub fn generate_strings_with_ffi_consts(
43    model: &ApiModel,
44    ffi_consts: Option<&HashSet<String>>,
45) -> GeneratedFiles {
46    let c_prefix = model.c_prefix.clone();
47    let enums = format_rust_source(&emit_enums(model, &c_prefix, ffi_consts));
48    let structs = format_rust_source(&emit_structs(model, &c_prefix));
49    let extensions = format_rust_source(&emit_extensions(model, &c_prefix));
50    let objects = format_rust_source(&emit_objects(model, &c_prefix));
51    let callbacks = format_rust_source(&emit_callbacks(model, &c_prefix));
52    let functions = format_rust_source(&emit_functions(model, &c_prefix));
53    let constants = format_rust_source(&emit_constants(model));
54    let mod_rs = format_rust_source(&emit_mod_rs());
55
56    GeneratedFiles {
57        enums,
58        structs,
59        extensions,
60        objects,
61        callbacks,
62        functions,
63        constants,
64        mod_rs,
65    }
66}
67
68pub fn generate_to_dir(model: &ApiModel, out_dir: &Path) -> std::io::Result<()> {
69    let files = generate_strings(model);
70    files.write_to_dir(out_dir)
71}
72
73fn emit_mod_rs() -> String {
74    let content = r#"#![allow(dead_code, unused_imports)]
75
76mod enums;
77mod structs;
78mod extensions;
79mod objects;
80mod callbacks;
81mod functions;
82mod constants;
83
84pub use enums::*;
85pub use structs::*;
86pub use extensions::*;
87pub use objects::*;
88pub use callbacks::*;
89pub use functions::*;
90pub use constants::*;
91"#;
92
93    content
94        .replacen(
95            "#![allow(dead_code, unused_imports)]",
96            "#[allow(dead_code, unused_imports)]",
97            1,
98        )
99        .to_string()
100}
101
102fn format_rust_source(source: &str) -> String {
103    match syn::parse_file(source) {
104        Ok(file) => prettyplease::unparse(&file),
105        Err(_) => source.to_string(),
106    }
107}
108
109fn emit_enums(model: &ApiModel, c_prefix: &str, ffi_consts: Option<&HashSet<String>>) -> String {
110    let mut out = String::new();
111    out.push_str(
112        r#"#![allow(dead_code, unused_imports)]
113
114use crate::ffi;
115use bitflags::bitflags;
116
117"#,
118    );
119
120    for e in &model.enums {
121        out.push_str(&emit_enum(e, c_prefix, ffi_consts));
122    }
123    for b in &model.bitmasks {
124        out.push_str(&emit_bitmask(b, c_prefix, ffi_consts));
125    }
126
127    out
128}
129
130fn emit_structs(model: &ApiModel, c_prefix: &str) -> String {
131    let mut out = String::new();
132    out.push_str(&format!(
133        r#"#![allow(dead_code, unused_imports)]
134
135use crate::ffi;
136use super::*;
137use std::any::Any;
138use std::ffi::CStr;
139
140fn string_view_to_string(view: ffi::{prefix}StringView) -> String {{
141    if view.data.is_null() || view.length == 0 {{
142        return String::new();
143    }}
144    let data = view.data.cast::<u8>();
145    let slice = unsafe {{ std::slice::from_raw_parts(data, view.length) }};
146    String::from_utf8_lossy(slice).into_owned()
147}}
148
149"#,
150        prefix = c_prefix
151    ));
152
153    let index = TypeIndex::new(model);
154    let constant_map = build_constant_map(model);
155    let callback_info_map = build_callback_info_map(model);
156    let callback_info_mode_map = build_callback_info_mode_map(model);
157    for s in &model.structures {
158        out.push_str(&emit_struct(
159            s,
160            &index,
161            c_prefix,
162            &callback_info_map,
163            &callback_info_mode_map,
164            &constant_map,
165        ));
166    }
167
168    out
169}
170
171fn emit_extensions(model: &ApiModel, c_prefix: &str) -> String {
172    let mut out = String::new();
173    out.push_str(&format!(
174        r#"#![allow(dead_code, unused_imports)]
175
176use crate::ffi;
177use super::*;
178use std::any::Any;
179
180pub(crate) struct ChainedStructStorage {{
181    entries: Vec<Box<ffi::{prefix}ChainedStruct>>,
182    buffers: Vec<Box<dyn Any>>,
183    nested: Vec<ChainedStructStorage>,
184}}
185
186impl ChainedStructStorage {{
187    pub(crate) fn new() -> Self {{
188        Self {{
189            entries: Vec::new(),
190            buffers: Vec::new(),
191            nested: Vec::new(),
192        }}
193    }}
194
195    pub(crate) fn push(
196        &mut self,
197        s_type: ffi::{prefix}SType,
198        next: *mut ffi::{prefix}ChainedStruct,
199    ) -> *mut ffi::{prefix}ChainedStruct {{
200        let mut node = Box::new(ffi::{prefix}ChainedStruct {{ next, sType: s_type }});
201        let ptr = std::ptr::from_mut(node.as_mut());
202        self.entries.push(node);
203        ptr
204    }}
205
206    pub(crate) fn push_value<T: 'static>(&mut self, value: T) -> *const T {{
207        let boxed = Box::new(value);
208        let ptr = std::ptr::from_ref(boxed.as_ref());
209        self.buffers.push(boxed);
210        ptr
211    }}
212
213    pub(crate) fn push_value_mut<T: 'static>(&mut self, value: T) -> *mut T {{
214        let mut boxed = Box::new(value);
215        let ptr = std::ptr::from_mut(boxed.as_mut());
216        self.buffers.push(boxed);
217        ptr
218    }}
219
220    pub(crate) fn push_vec<T: 'static>(&mut self, value: Vec<T>) -> *const T {{
221        let ptr = value.as_ptr();
222        self.buffers.push(Box::new(value));
223        ptr
224    }}
225
226    pub(crate) fn push_vec_mut<T: 'static>(&mut self, value: Vec<T>) -> *mut T {{
227        let mut value = value;
228        let ptr = value.as_mut_ptr();
229        self.buffers.push(Box::new(value));
230        ptr
231    }}
232
233    pub(crate) fn push_any<T: 'static>(&mut self, value: T) {{
234        self.buffers.push(Box::new(value));
235    }}
236
237    pub(crate) fn push_storage(&mut self, storage: ChainedStructStorage) {{
238        self.nested.push(storage);
239    }}
240}}
241
242"#,
243        prefix = c_prefix
244    ));
245
246    let mut roots: HashMap<String, Vec<&StructureModel>> = HashMap::new();
247    let mut extensible_roots: Vec<String> = Vec::new();
248    for s in &model.structures {
249        if s.def.extensible.is_extensible() {
250            extensible_roots.push(s.name.clone());
251        }
252        for root in &s.def.chain_roots {
253            roots.entry(root.clone()).or_default().push(s);
254        }
255    }
256
257    let mut root_names: Vec<String> = extensible_roots;
258    root_names.sort();
259    root_names.dedup();
260
261    let stype_map = build_stype_map(model, c_prefix);
262
263    for root in root_names {
264        let variants = roots.get(&root).cloned().unwrap_or_default();
265        let enum_name = format!("{}Extension", type_name(&root));
266
267        let mut variant_lines = Vec::new();
268        let mut from_lines = Vec::new();
269        for s in variants.iter() {
270            let ty = type_name(&s.name);
271            variant_lines.push(format!(r#"{ty}({ty}),"#));
272            from_lines.push(format!(
273                r#"impl std::convert::From<{ty}> for {enum_name} {{
274                    fn from(ext: {ty}) -> Self {{
275                        {enum_name}::{ty}(ext)
276                    }}
277                }}"#
278            ));
279        }
280        let variant_block = variant_lines.join("\n");
281        let from_block = from_lines.join("\n");
282
283        let has_variants = !variants.is_empty();
284        let impl_block = if has_variants {
285            format!(
286                r#"impl {enum_name} {{
287    pub(crate) fn push_chain(
288        &self,
289        storage: &mut ChainedStructStorage,
290        next: *mut ffi::{prefix}ChainedStruct,
291    ) -> *mut ffi::{prefix}ChainedStruct {{
292        match self {{
293{push_arms}
294        }}
295    }}
296}}
297
298"#,
299                enum_name = enum_name,
300                prefix = c_prefix,
301                push_arms = {
302                    let mut arms = Vec::new();
303                    for s in variants.iter() {
304                        let ty = type_name(&s.name);
305                        let stype_const = stype_map.get(&s.name).cloned().unwrap_or_else(|| {
306                            format!(
307                                r#"{}::{}"#,
308                                type_name("s type"),
309                                enum_variant_name_camel(&s.name)
310                            )
311                        });
312                        arms.push(format!(
313                            r#"            {enum_name}::{ty}(value) => {{
314                let (mut raw, storage_value) = value.to_ffi();
315                raw.chain.sType = {stype_const} as ffi::{prefix}SType;
316                raw.chain.next = next;
317                storage.push_storage(storage_value);
318                let raw_ptr = storage.push_value_mut(raw);
319                raw_ptr.cast::<ffi::{prefix}ChainedStruct>()
320            }}"#,
321                            enum_name = enum_name,
322                            ty = ty,
323                            stype_const = stype_const,
324                            prefix = c_prefix
325                        ));
326                    }
327                    arms.join("\n")
328                }
329            )
330        } else {
331            format!(
332                r#"impl {enum_name} {{
333    pub(crate) fn push_chain(
334        &self,
335        storage: &mut ChainedStructStorage,
336        next: *mut ffi::{prefix}ChainedStruct,
337    ) -> *mut ffi::{prefix}ChainedStruct {{
338        let _ = self;
339        let _ = storage;
340        next
341    }}
342}}
343
344"#,
345                enum_name = enum_name,
346                prefix = c_prefix
347            )
348        };
349
350        out.push_str(&format!(
351            r#"#[allow(dead_code)]
352pub enum {enum_name} {{
353{variants}
354}}
355
356{from_block}
357
358{impl_block}
359"#,
360            enum_name = enum_name,
361            variants = variant_block,
362            impl_block = impl_block
363        ));
364    }
365
366    out
367}
368
369fn emit_objects(model: &ApiModel, c_prefix: &str) -> String {
370    let mut out = String::new();
371    out.push_str(
372        r#"#![allow(dead_code, unused_imports)]
373
374use crate::ffi;
375use super::*;
376
377"#,
378    );
379
380    let constructors = build_constructor_map(model);
381    let index = TypeIndex::new(model);
382    for o in &model.objects {
383        out.push_str(&emit_object(
384            o,
385            constructors.get(&o.name),
386            model,
387            &index,
388            c_prefix,
389        ));
390    }
391
392    out
393}
394
395fn emit_callbacks(model: &ApiModel, c_prefix: &str) -> String {
396    let mut out = String::new();
397    out.push_str(&format!(
398        r#"#![allow(dead_code, unused_imports)]
399
400use crate::ffi;
401use super::*;
402use std::cell::RefCell;
403
404fn string_view_to_string(view: ffi::{prefix}StringView) -> String {{
405    if view.data.is_null() || view.length == 0 {{
406        return String::new();
407    }}
408    let data = view.data.cast::<u8>();
409    let slice = unsafe {{ std::slice::from_raw_parts(data, view.length) }};
410    String::from_utf8_lossy(slice).into_owned()
411}}
412
413"#,
414        prefix = c_prefix
415    ));
416
417    let index = TypeIndex::new(model);
418
419    for fp in &model.function_pointers {
420        out.push_str(&emit_function_pointer(fp));
421    }
422
423    for c in &model.callback_functions {
424        out.push_str(&emit_callback_function(c, &index, c_prefix));
425    }
426
427    for c in &model.callbacks {
428        out.push_str(&emit_callback(c));
429    }
430
431    for c in &model.callback_infos {
432        out.push_str(&emit_callback_info(c, &index));
433    }
434
435    out
436}
437
438fn emit_functions(model: &ApiModel, c_prefix: &str) -> String {
439    let mut out = String::new();
440    out.push_str(
441        r#"#![allow(dead_code, unused_imports)]
442
443use crate::ffi;
444use super::*;
445
446"#,
447    );
448
449    let index = TypeIndex::new(model);
450    for f in &model.functions {
451        out.push_str(&emit_function(f, model, &index, c_prefix));
452    }
453
454    out
455}
456
457fn emit_constants(model: &ApiModel) -> String {
458    let mut out = String::new();
459    out.push_str(
460        r#"#![allow(dead_code, unused_imports)]
461
462use super::*;
463
464"#,
465    );
466
467    for c in &model.constants {
468        if let Some(block) = emit_constant(c) {
469            out.push_str(&block);
470        }
471    }
472
473    out
474}
475
476fn emit_enum(e: &EnumModel, c_prefix: &str, ffi_consts: Option<&HashSet<String>>) -> String {
477    let name = type_name(&e.name);
478    let mut variants = Vec::new();
479    let mut from_arms = Vec::new();
480    let ffi_type = ffi_type_name(&e.name, c_prefix);
481    let mut first_variant: Option<String> = None;
482
483    for v in &e.def.values {
484        let variant = enum_variant_name_camel(&v.name);
485        let const_variant = ffi_enum_value_name(&v.name);
486        let const_name = ffi_enum_const_name(&ffi_type, &const_variant);
487        if !ffi_const_is_available(ffi_consts, &const_name) {
488            continue;
489        }
490        variants.push(format!(
491            r#"    {variant} = ffi::{const_name} as u32,"#,
492            variant = variant,
493            const_name = const_name
494        ));
495        from_arms.push(format!(
496            r#"            ffi::{const_name} => {name}::{variant},"#,
497            const_name = const_name,
498            name = name,
499            variant = variant
500        ));
501        if first_variant.is_none() {
502            first_variant = Some(variant);
503        }
504    }
505
506    let variants_block = variants.join("\n");
507    let from_arms_block = from_arms.join("\n");
508    let fallback_variant = first_variant.unwrap_or_else(|| "Undefined".to_string());
509
510    format!(
511        r#"#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
512#[repr(u32)]
513pub enum {name} {{
514{variants}
515}}
516
517impl From<ffi::{ffi_type}> for {name} {{
518    fn from(value: ffi::{ffi_type}) -> Self {{
519        match value as u32 {{
520{from_arms}
521            _ => {name}::{fallback},
522        }}
523    }}
524}}
525
526impl From<{name}> for ffi::{ffi_type} {{
527    fn from(value: {name}) -> Self {{
528        value as ffi::{ffi_type}
529    }}
530}}
531
532"#,
533        name = name,
534        variants = variants_block,
535        ffi_type = ffi_type,
536        from_arms = from_arms_block,
537        fallback = fallback_variant
538    )
539}
540
541fn emit_bitmask(b: &BitmaskModel, c_prefix: &str, ffi_consts: Option<&HashSet<String>>) -> String {
542    let name = type_name(&b.name);
543    let mut variants = Vec::new();
544    let ffi_type = ffi_type_name(&b.name, c_prefix);
545
546    for v in &b.def.values {
547        let variant = bitmask_variant_name(&v.name);
548        let const_variant = ffi_enum_value_name(&v.name);
549        let const_name = ffi_bitmask_const_name(&ffi_type, &const_variant);
550        if !ffi_const_is_available(ffi_consts, &const_name) {
551            continue;
552        }
553        variants.push(format!(
554            r#"        const {variant} = ffi::{const_name} as u64;"#,
555            variant = variant,
556            const_name = const_name
557        ));
558    }
559
560    let variants_block = variants.join("\n");
561
562    format!(
563        r#"bitflags! {{
564    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
565    pub struct {name}: u64 {{
566{variants}
567    }}
568}}
569
570impl From<ffi::{ffi_type}> for {name} {{
571    fn from(value: ffi::{ffi_type}) -> Self {{
572        {name}::from_bits_truncate(value as u64)
573    }}
574}}
575
576impl From<{name}> for ffi::{ffi_type} {{
577    fn from(value: {name}) -> Self {{
578        value.bits() as ffi::{ffi_type}
579    }}
580}}
581
582"#,
583        name = name,
584        variants = variants_block,
585        ffi_type = ffi_type
586    )
587}
588
589fn ffi_const_is_available(ffi_consts: Option<&HashSet<String>>, name: &str) -> bool {
590    ffi_consts.map_or(true, |set| set.contains(name))
591}
592
593fn enum_value_u32(value: &EnumValueDef) -> String {
594    enum_value_u64(value).to_string()
595}
596
597fn enum_value_u64(value: &EnumValueDef) -> u64 {
598    let base = match &value.value {
599        Value::Number(num) => num.as_u64().unwrap_or(0),
600        Value::String(s) => parse_numeric_string_u64(s).unwrap_or(0),
601        _ => 0,
602    };
603    let offset = enum_tag_offset(&value.tags);
604    base + offset as u64
605}
606
607fn enum_tag_offset(tags: &[String]) -> u32 {
608    let has_compat = tags.iter().any(|t| t == "compat");
609    let has_emscripten = tags.iter().any(|t| t == "emscripten");
610    let has_dawn = tags.iter().any(|t| t == "dawn");
611    if has_compat {
612        return 0x0002_0000;
613    }
614    if has_emscripten {
615        return 0x0004_0000;
616    }
617    if has_dawn {
618        return 0x0005_0000;
619    }
620    if tags.iter().any(|t| t == "native") {
621        return 0x0001_0000;
622    }
623    0
624}
625
626fn parse_numeric_string_u64(value: &str) -> Option<u64> {
627    let trimmed = value.trim();
628    if let Some(hex) = trimmed.strip_prefix("0x") {
629        u64::from_str_radix(hex, 16).ok()
630    } else if let Some(hex) = trimmed.strip_prefix("0X") {
631        u64::from_str_radix(hex, 16).ok()
632    } else {
633        trimmed.parse::<u64>().ok()
634    }
635}
636
637fn emit_struct(
638    s: &StructureModel,
639    index: &TypeIndex,
640    c_prefix: &str,
641    callback_info_map: &HashMap<String, String>,
642    callback_info_mode_map: &HashMap<String, bool>,
643    constant_map: &HashMap<String, String>,
644) -> String {
645    let name = type_name(&s.name);
646    let ffi_name = ffi_type_name(&s.name, c_prefix);
647
648    let mut fields = Vec::new();
649    let mut default_fields = Vec::new();
650    let mut extra_methods = Vec::new();
651    let length_fields = length_field_names(&s.def.members);
652    let needs_free_members = index.struct_needs_free_members(&s.name);
653
654    if s.def.extensible.is_extensible() {
655        let ext_enum = format!("{}Extension", type_name(&s.name));
656        fields.push(format!(
657            r#"pub(crate) extensions: Vec<{ext_enum}>,"#,
658            ext_enum = ext_enum
659        ));
660        default_fields.push("extensions: Vec::new(),".to_string());
661        let to_ffi_body = emit_struct_to_ffi_body(
662            s,
663            index,
664            c_prefix,
665            true,
666            callback_info_map,
667            callback_info_mode_map,
668        );
669        extra_methods.push(format!(
670            r#"pub(crate) fn to_ffi(&self) -> (ffi::{ffi_name}, ChainedStructStorage) {{
671        let mut storage = ChainedStructStorage::new();
672        let mut next: *mut ffi::{c_prefix}ChainedStruct = std::ptr::null_mut();
673        for ext in self.extensions.iter().rev() {{
674            next = ext.push_chain(&mut storage, next);
675        }}
676{to_ffi_body}
677    }}"#,
678        ));
679        extra_methods.push(format!(
680            r#"pub fn with_extension(mut self, extension: {ext_enum}) -> Self {{
681            self.extensions.push(extension);
682            self
683        }}"#
684        ));
685    } else {
686        let to_ffi_body = emit_struct_to_ffi_body(
687            s,
688            index,
689            c_prefix,
690            false,
691            callback_info_map,
692            callback_info_mode_map,
693        );
694        extra_methods.push(
695            format!(
696                r#"    pub(crate) fn to_ffi(&self) -> (ffi::{ffi_name}, ChainedStructStorage) {{
697        let mut storage = ChainedStructStorage::new();
698{body}
699    }}"#,
700                ffi_name = ffi_name,
701                body = indent_block(&to_ffi_body, 8)
702            )
703            .to_string(),
704        );
705    }
706
707    for member in &s.def.members {
708        if length_fields.contains(&member.name) {
709            continue;
710        }
711        let field_name = safe_ident(&snake_case_name(&member.name));
712        let field_ty = struct_field_type(member, index);
713        let param_ty = builder_param_type(member, index);
714        fields.push(format!(
715            r#"pub {field_name}: {field_ty},"#,
716            field_name = field_name,
717            field_ty = field_ty
718        ));
719        let default_value = member_default_expr(member, index, c_prefix, constant_map)
720            .map(|expr| format!("Some({expr})", expr = expr))
721            .unwrap_or_else(|| "None".to_string());
722        default_fields.push(format!(
723            r#"        {field_name}: {default_value},"#,
724            field_name = field_name,
725            default_value = default_value
726        ));
727
728        let _ = param_ty;
729    }
730
731    if needs_free_members {
732        fields.push(format!(
733            r#"#[doc(hidden)]
734    pub(crate) _free_members: Option<ffi::{ffi_name}>,"#,
735            ffi_name = ffi_name
736        ));
737        default_fields.push("        _free_members: None,".to_string());
738    }
739
740    let fields_block = fields.join("\n");
741    let default_fields_block = default_fields.join("\n");
742    let mut from_ffi_body = emit_struct_from_ffi_body(s, index);
743    if from_ffi_body.is_empty() {
744        from_ffi_body = "let _ = value;\n        Self::default()".to_string();
745    }
746    let from_ffi = format!(
747        r#"    pub(crate) fn from_ffi(value: ffi::{ffi_name}) -> Self {{
748{from_ffi_body}
749    }}"#,
750        ffi_name = ffi_name,
751        from_ffi_body = indent_block(&from_ffi_body, 8)
752    );
753
754    let mut free_members = String::new();
755    if needs_free_members {
756        let free_fn = free_members_fn_name(&s.name);
757        free_members = format!(
758            r#"    pub(crate) fn free_members(value: ffi::{ffi_name}) {{
759        unsafe {{ ffi::{free_fn}(value) }};
760    }}"#,
761            ffi_name = ffi_name,
762            free_fn = free_fn
763        );
764    }
765
766    let mut extra_blocks = extra_methods.clone();
767    if !from_ffi.is_empty() {
768        extra_blocks.push(from_ffi);
769    }
770    if !free_members.is_empty() {
771        extra_blocks.push(free_members);
772    }
773    let extra_methods_block = extra_blocks.join("\n\n");
774    let drop_impl = if needs_free_members {
775        let free_fn = free_members_fn_name(&s.name);
776        format!(
777            r#"impl Drop for {name} {{
778    fn drop(&mut self) {{
779        if let Some(value) = self._free_members.take() {{
780            unsafe {{ ffi::{free_fn}(value) }};
781        }}
782    }}
783}}
784
785"#,
786            name = name,
787            free_fn = free_fn
788        )
789    } else {
790        String::new()
791    };
792
793    let struct_block = format!(
794        r#"pub struct {name} {{
795{fields}
796}}
797
798impl Default for {name} {{
799    fn default() -> Self {{
800        Self {{
801{defaults}
802        }}
803    }}
804}}
805
806impl {name} {{
807    pub fn new() -> Self {{
808        Self::default()
809    }}
810{extra_methods}
811}}
812
813"#,
814        name = name,
815        fields = fields_block,
816        defaults = default_fields_block,
817        extra_methods = extra_methods_block
818    );
819    format!(
820        "{struct_block}{drop_impl}",
821        struct_block = struct_block,
822        drop_impl = drop_impl
823    )
824}
825
826fn emit_object(
827    o: &ObjectModel,
828    constructor: Option<&FunctionModel>,
829    model: &ApiModel,
830    index: &TypeIndex,
831    c_prefix: &str,
832) -> String {
833    let name = type_name(&o.name);
834    let mut methods = Vec::new();
835    let no_autolock = o.def.no_autolock.unwrap_or(false);
836    let marker_field = if no_autolock {
837        "    _not_sync: std::marker::PhantomData<std::cell::Cell<()>>,\n"
838    } else {
839        ""
840    };
841    let marker_init = if no_autolock {
842        ", _not_sync: std::marker::PhantomData"
843    } else {
844        ""
845    };
846    let send_sync_impl = if no_autolock {
847        String::new()
848    } else {
849        format!(
850            r#"unsafe impl Send for {name} {{}}
851
852unsafe impl Sync for {name} {{}}
853
854"#,
855            name = name
856        )
857    };
858
859    if let Some(func) = constructor {
860        let signature = fn_signature_params(&func.def.args, model, None);
861        let (arg_prelude, ffi_args, has_callback) =
862            emit_ffi_arg_prelude(&func.def.args, model, index, c_prefix);
863        let func_name = ffi_fn_name(&func.name, c_prefix);
864        let args = ffi_args.join(", ");
865        let ffi_call = format!(
866            "        let result = unsafe {{ ffi::{func}({args}) }};",
867            func = func_name,
868            args = args
869        );
870        let ret = emit_return_conversion(func.def.returns(), index, "result");
871        let postlude = emit_out_struct_postlude(&func.def.args, index);
872        let body = if postlude.is_empty() {
873            format!("{}\n{}", ffi_call, ret)
874        } else {
875            format!("{}\n{}\n{}", ffi_call, postlude, ret)
876        };
877        methods.push(format!(
878            r#"    pub fn new({signature}) -> Self {{
879{arg_prelude}
880{body}
881    }}"#,
882            signature = signature,
883            arg_prelude = indent_block(&arg_prelude, 8),
884            body = if has_callback {
885                "        unimplemented!()".to_string()
886            } else {
887                body
888            }
889        ));
890    }
891
892    for method in &o.def.methods {
893        let method_name = safe_ident(&snake_case_name(&method.name));
894        let return_ty = method
895            .returns()
896            .map(|ret| rust_return_type(ret))
897            .unwrap_or_else(|| "()".to_string());
898
899        let signature = fn_signature_params(&method.args, model, Some("self"));
900        let (arg_prelude, ffi_args, has_callback) =
901            emit_ffi_arg_prelude(&method.args, model, index, c_prefix);
902        let postlude = emit_out_struct_postlude(&method.args, index);
903
904        methods.push(format!(
905            r#"    pub fn {method_name}({signature}) -> {return_ty} {{
906{arg_prelude}
907{body}
908    }}"#,
909            method_name = method_name,
910            signature = signature,
911            return_ty = return_ty,
912            arg_prelude = indent_block(&arg_prelude, 8),
913            body = if has_callback {
914                "        unimplemented!()".to_string()
915            } else {
916                let args = if ffi_args.is_empty() {
917                    "".to_string()
918                } else {
919                    format!(", {}", ffi_args.join(", "))
920                };
921                if method
922                    .returns()
923                    .map(|ret| ret.get_type() == "void")
924                    .unwrap_or(true)
925                {
926                    let func_name = ffi_fn_name(&format!("{} {}", o.name, method.name), c_prefix);
927                    let postlude = if postlude.is_empty() {
928                        String::new()
929                    } else {
930                        format!("\n{postlude}", postlude = postlude)
931                    };
932                    format!(
933                        "        unsafe {{ ffi::{func}(self.raw{args}) }};{postlude}\n        ()",
934                        func = func_name,
935                        args = args,
936                        postlude = postlude
937                    )
938                } else {
939                    let func_name = ffi_fn_name(&format!("{} {}", o.name, method.name), c_prefix);
940                    let ffi_call = format!(
941                        "        let result = unsafe {{ ffi::{func}(self.raw{args}) }};",
942                        func = func_name,
943                        args = args
944                    );
945                    let ret = emit_return_conversion(method.returns(), index, "result");
946                    if postlude.is_empty() {
947                        format!("{}\n{}", ffi_call, ret)
948                    } else {
949                        format!("{}\n{}\n{}", ffi_call, postlude, ret)
950                    }
951                }
952            }
953        ));
954    }
955
956    let methods_block = methods.join("\n\n");
957
958    format!(
959        r#"#[derive(Debug)]
960pub struct {name} {{
961    raw: ffi::{prefix}{name},
962{marker_field}}}
963
964impl {name} {{
965    pub(crate) unsafe fn from_raw(raw: ffi::{prefix}{name}) -> Self {{
966        Self {{ raw{marker_init} }}
967    }}
968
969    pub fn as_raw(&self) -> ffi::{prefix}{name} {{
970        self.raw
971    }}
972
973{methods}
974}}
975
976impl Drop for {name} {{
977    fn drop(&mut self) {{
978        if self.as_raw().is_null() {{
979            return;
980        }}
981        unsafe {{ ffi::wgpu{name}Release(self.raw) }};
982    }}
983}}
984
985impl Clone for {name} {{
986    fn clone(&self) -> Self {{
987        unsafe {{ ffi::wgpu{name}AddRef(self.raw) }};
988        Self {{ raw: self.raw{marker_init} }}
989    }}
990}}
991
992{send_sync_impl}"#,
993        name = name,
994        methods = methods_block,
995        prefix = c_prefix,
996        marker_field = marker_field,
997        marker_init = marker_init,
998        send_sync_impl = send_sync_impl
999    )
1000}
1001
1002fn emit_callback_function(c: &CallbackFunctionModel, index: &TypeIndex, c_prefix: &str) -> String {
1003    let name = callback_type_name(&c.name);
1004    let args = callback_arg_list(&c.def.args);
1005    let signature = format!(r#"{args}"#, args = args);
1006    let trampoline = callback_trampoline_name(&c.name);
1007    let ffi_args = callback_ffi_arg_list(&c.def.args, index, c_prefix);
1008
1009    let mut conversions = Vec::new();
1010    let mut call_args = Vec::new();
1011
1012    for arg in &c.def.args {
1013        let arg_name = safe_ident(&snake_case_name(&arg.name));
1014        let mut call_arg = arg_name.clone();
1015
1016        if arg.member_type == "string view" {
1017            conversions.push(format!(
1018                r#"    let {arg_name} = string_view_to_string({arg_name});"#,
1019                arg_name = arg_name
1020            ));
1021            call_args.push(call_arg);
1022            continue;
1023        }
1024
1025        if index.is_enum(&arg.member_type) || index.is_bitmask(&arg.member_type) {
1026            conversions.push(format!(
1027                r#"    let {arg_name} = {arg_name}.into();"#,
1028                arg_name = arg_name
1029            ));
1030            call_args.push(call_arg);
1031            continue;
1032        }
1033
1034        if index.is_object(&arg.member_type) {
1035            let obj = type_name(&arg.member_type);
1036            if arg.length.is_some() {
1037                let len_expr = length_value_expr(arg.length.as_ref().unwrap());
1038                conversions.push(format!(
1039                    r#"    let {arg_name} = if {arg_name}.is_null() {{
1040        Vec::new()
1041    }} else {{
1042        unsafe {{ std::slice::from_raw_parts({arg_name}, {len_expr}) }}
1043            .iter()
1044            .map(|raw| unsafe {{ {obj}::from_raw(*raw) }})
1045            .collect()
1046    }};"#,
1047                    arg_name = arg_name,
1048                    len_expr = len_expr,
1049                    obj = obj
1050                ));
1051                call_args.push(call_arg);
1052                continue;
1053            }
1054            if arg.optional {
1055                conversions.push(format!(
1056                    r#"    let {arg_name} = if {arg_name}.is_null() {{
1057        None
1058    }} else {{
1059        Some(unsafe {{ {obj}::from_raw({arg_name}) }})
1060    }};"#,
1061                    arg_name = arg_name,
1062                    obj = obj
1063                ));
1064            } else {
1065                conversions.push(format!(
1066                    r#"    let {arg_name} = unsafe {{ {obj}::from_raw({arg_name}) }};"#,
1067                    arg_name = arg_name,
1068                    obj = obj
1069                ));
1070            }
1071            call_args.push(call_arg);
1072            continue;
1073        }
1074
1075        if index.struct_extensible(&arg.member_type).is_some()
1076            && (arg.annotation.is_const_ptr() || arg.annotation.is_mut_ptr())
1077        {
1078            let rust_ty = type_name(&arg.member_type);
1079            conversions.push(format!(
1080                r#"    let {arg_name} = if {arg_name}.is_null() {{
1081        {rust_ty}::new()
1082    }} else {{
1083        {rust_ty}::new()
1084    }};"#,
1085                arg_name = arg_name,
1086                rust_ty = rust_ty
1087            ));
1088            if arg.annotation.is_const_ptr() {
1089                call_arg = format!(r#"&{arg_name}"#, arg_name = arg_name);
1090            } else if arg.annotation.is_mut_ptr() {
1091                call_arg = format!(r#"&mut {arg_name}"#, arg_name = arg_name);
1092            }
1093            call_args.push(call_arg);
1094            continue;
1095        }
1096
1097        if arg.member_type == "bool" {
1098            conversions.push(format!(
1099                r#"    let {arg_name} = {arg_name} != 0;"#,
1100                arg_name = arg_name
1101            ));
1102            call_args.push(call_arg);
1103            continue;
1104        }
1105
1106        call_args.push(call_arg);
1107    }
1108
1109    let conversions_block = if conversions.is_empty() {
1110        String::new()
1111    } else {
1112        conversions.join("\n")
1113    };
1114    let call_args_block = call_args.join(", ");
1115
1116    format!(
1117        r#"pub type {name} = Box<dyn FnMut({signature}) + Send + 'static>;
1118
1119pub(crate) unsafe extern "C" fn {trampoline}({ffi_args}, userdata1: *mut std::ffi::c_void, userdata2: *mut std::ffi::c_void) {{
1120    let _ = userdata2;
1121{conversions}
1122    let mut callback = unsafe {{ Box::from_raw(userdata1.cast::<Option<{name}>>()) }};
1123    if let Some(mut callback) = callback.take() {{
1124        callback({call_args});
1125    }}
1126}}
1127
1128"#,
1129        name = name,
1130        signature = signature,
1131        trampoline = trampoline,
1132        ffi_args = ffi_args,
1133        conversions = conversions_block,
1134        call_args = call_args_block
1135    )
1136}
1137
1138fn emit_callback(c: &CallbackModel) -> String {
1139    let name = callback_type_name(&c.name);
1140    let args = callback_arg_list(c.def.args());
1141    if let Some(ret) = c.def.returns() {
1142        let ret_ty = rust_return_type(ret);
1143        return format!(
1144            r#"pub type {name} = Box<dyn FnMut({args}) -> {ret_ty} + Send + 'static>;
1145
1146"#,
1147            name = name,
1148            args = args,
1149            ret_ty = ret_ty
1150        );
1151    }
1152
1153    format!(
1154        r#"pub type {name} = Box<dyn FnMut({args}) + Send + 'static>;
1155
1156"#,
1157        name = name,
1158        args = args
1159    )
1160}
1161
1162fn emit_callback_info(c: &CallbackInfoModel, index: &TypeIndex) -> String {
1163    let name = type_name(&c.name);
1164
1165    let mut fields = Vec::new();
1166
1167    for member in &c.def.members {
1168        let field_name = safe_ident(&snake_case_name(&member.name));
1169        let field_ty = if member.name == "callback" {
1170            format!(
1171                r#"std::cell::RefCell<Option<{}>>"#,
1172                callback_type_name(&member.member_type)
1173            )
1174        } else {
1175            struct_field_type(member, index)
1176        };
1177        let param_ty = builder_param_type(member, index);
1178        fields.push(format!(
1179            r#"    pub {field_name}: {field_ty},"#,
1180            field_name = field_name,
1181            field_ty = field_ty
1182        ));
1183
1184        let _ = param_ty;
1185    }
1186
1187    let fields_block = fields.join("\n");
1188    let mut defaults = Vec::new();
1189    for member in &c.def.members {
1190        let field_name = safe_ident(&snake_case_name(&member.name));
1191        if member.name == "callback" {
1192            defaults.push(format!(
1193                r#"            {field_name}: std::cell::RefCell::new(None),"#,
1194                field_name = field_name
1195            ));
1196        } else {
1197            defaults.push(format!(
1198                r#"            {field_name}: None,"#,
1199                field_name = field_name
1200            ));
1201        }
1202    }
1203    let defaults_block = defaults.join("\n");
1204
1205    format!(
1206        r#"pub struct {name} {{
1207{fields}
1208}}
1209
1210impl Default for {name} {{
1211    fn default() -> Self {{
1212        Self {{
1213{defaults}
1214        }}
1215    }}
1216}}
1217
1218impl {name} {{
1219    pub fn new() -> Self {{
1220        Self::default()
1221    }}
1222}}
1223
1224"#,
1225        name = name,
1226        fields = fields_block,
1227        defaults = defaults_block,
1228    )
1229}
1230
1231fn emit_function_pointer(fp: &crate::api_model::FunctionPointerModel) -> String {
1232    let name = type_name(&fp.name);
1233    let args = callback_arg_list(fp.def.args());
1234    let ret = fp
1235        .def
1236        .returns()
1237        .map(rust_return_type)
1238        .unwrap_or_else(|| "()".to_string());
1239
1240    format!(
1241        r#"pub type {name} = Option<unsafe extern "C" fn({args}) -> {ret}>;
1242
1243"#,
1244        name = name,
1245        args = args,
1246        ret = ret
1247    )
1248}
1249
1250fn emit_function(f: &FunctionModel, model: &ApiModel, index: &TypeIndex, c_prefix: &str) -> String {
1251    let name = safe_ident(&snake_case_name(&f.name));
1252    let return_ty = f
1253        .def
1254        .returns()
1255        .map(|ret| rust_return_type(ret))
1256        .unwrap_or_else(|| "()".to_string());
1257
1258    let signature = fn_signature_params(&f.def.args, model, None);
1259    let (arg_prelude, ffi_args, has_callback) =
1260        emit_ffi_arg_prelude(&f.def.args, model, index, c_prefix);
1261    let postlude = emit_out_struct_postlude(&f.def.args, index);
1262
1263    format!(
1264        r#"pub fn {name}({signature}) -> {return_ty} {{
1265{arg_prelude}
1266{body}
1267}}
1268
1269"#,
1270        name = name,
1271        signature = signature,
1272        return_ty = return_ty,
1273        arg_prelude = indent_block(&arg_prelude, 4),
1274        body = if has_callback {
1275            "    unimplemented!()".to_string()
1276        } else {
1277            if f.def
1278                .returns()
1279                .map(|ret| ret.get_type() == "void")
1280                .unwrap_or(true)
1281            {
1282                let func_name = ffi_fn_name(&f.name, c_prefix);
1283                let postlude = if postlude.is_empty() {
1284                    String::new()
1285                } else {
1286                    format!("\n{postlude}", postlude = postlude)
1287                };
1288                format!(
1289                    "    unsafe {{ ffi::{func}({args}) }};{postlude}\n    ()",
1290                    func = func_name,
1291                    args = ffi_args.join(", "),
1292                    postlude = postlude
1293                )
1294            } else {
1295                let func_name = ffi_fn_name(&f.name, c_prefix);
1296                let ffi_call = format!(
1297                    "    let result = unsafe {{ ffi::{func}({args}) }};",
1298                    func = func_name,
1299                    args = ffi_args.join(", ")
1300                );
1301                let ret = emit_return_conversion(f.def.returns(), index, "result");
1302                if postlude.is_empty() {
1303                    format!("{}\n{}", ffi_call, ret)
1304                } else {
1305                    format!("{}\n{}\n{}", ffi_call, postlude, ret)
1306                }
1307            }
1308        }
1309    )
1310}
1311
1312fn emit_constant(c: &ConstantModel) -> Option<String> {
1313    let name = shouty_snake_case_name(&c.name);
1314    let ty = rust_type_for(&c.def.const_type);
1315    let value = match &c.def.value {
1316        Value::Number(num) => num.to_string(),
1317        Value::String(s) => {
1318            if let Some(parsed) = parse_numeric_string(s) {
1319                parsed.to_string()
1320            } else if let Some(parsed) = parse_numeric_string_u64(s) {
1321                parsed.to_string()
1322            } else {
1323                match s.as_str() {
1324                    "UINT32_MAX" => "u32::MAX".to_string(),
1325                    "UINT64_MAX" => "u64::MAX".to_string(),
1326                    "SIZE_MAX" => "usize::MAX".to_string(),
1327                    "NAN" => {
1328                        if ty == "f32" {
1329                            "f32::NAN".to_string()
1330                        } else {
1331                            "f64::NAN".to_string()
1332                        }
1333                    }
1334                    _ => return None,
1335                }
1336            }
1337        }
1338        _ => return None,
1339    };
1340
1341    Some(format!(
1342        r#"pub const {name}: {ty} = {value};
1343
1344"#,
1345        name = name,
1346        ty = ty,
1347        value = value
1348    ))
1349}
1350
1351fn build_constant_map(model: &ApiModel) -> HashMap<String, String> {
1352    let mut map = HashMap::new();
1353    for c in &model.constants {
1354        map.insert(c.name.clone(), shouty_snake_case_name(&c.name));
1355    }
1356    map
1357}
1358
1359fn build_constructor_map(model: &ApiModel) -> HashMap<String, FunctionModel> {
1360    let mut map = HashMap::new();
1361    for func in &model.functions {
1362        if let Some(obj_name) = func.name.strip_prefix("create ") {
1363            map.insert(obj_name.to_string(), func.clone());
1364        }
1365    }
1366    map
1367}
1368
1369fn callback_arg_list(args: &[RecordMember]) -> String {
1370    let mut parts = Vec::new();
1371    let length_fields = length_field_names(args);
1372    for arg in args {
1373        if length_fields.contains(&arg.name) {
1374            continue;
1375        }
1376        let arg_ty = callback_param_type(arg);
1377        parts.push(arg_ty);
1378    }
1379    parts.join(", ")
1380}
1381
1382fn callback_type_name(name: &str) -> String {
1383    let mut result = type_name(name);
1384    if !result.ends_with("Callback") {
1385        result.push_str("Callback");
1386    }
1387    result
1388}
1389
1390fn callback_trampoline_name(name: &str) -> String {
1391    format!(r#"{}_trampoline"#, safe_ident(&snake_case_name(name)))
1392}
1393
1394fn callback_ffi_arg_list(args: &[RecordMember], index: &TypeIndex, c_prefix: &str) -> String {
1395    let mut parts = Vec::new();
1396    for arg in args {
1397        let arg_name = safe_ident(&snake_case_name(&arg.name));
1398        let arg_ty = callback_ffi_arg_type(arg, index, c_prefix);
1399        parts.push(format!(
1400            r#"{arg_name}: {arg_ty}"#,
1401            arg_name = arg_name,
1402            arg_ty = arg_ty
1403        ));
1404    }
1405    parts.join(", ")
1406}
1407
1408fn callback_ffi_arg_type(arg: &RecordMember, index: &TypeIndex, c_prefix: &str) -> String {
1409    if arg.member_type == "string view" {
1410        return format!("ffi::{}StringView", c_prefix);
1411    }
1412
1413    if arg.member_type == "bool" {
1414        return format!("ffi::{}Bool", c_prefix);
1415    }
1416
1417    if index.is_object(&arg.member_type) {
1418        let base = format!("ffi::{}{}", c_prefix, type_name(&arg.member_type));
1419        if arg.annotation.is_mut_ptr() {
1420            return format!("*mut {base}", base = base);
1421        }
1422        if arg.annotation.is_const_ptr() || arg.length.is_some() {
1423            return format!("*const {base}", base = base);
1424        }
1425        return base;
1426    }
1427
1428    if index.is_enum(&arg.member_type) || index.is_bitmask(&arg.member_type) {
1429        let ffi_ty = ffi_type_name(&arg.member_type, c_prefix);
1430        return format!("ffi::{ffi_ty}", ffi_ty = ffi_ty);
1431    }
1432
1433    if index.struct_extensible(&arg.member_type).is_some() {
1434        let ffi_ty = ffi_type_name(&arg.member_type, c_prefix);
1435        let base = format!("ffi::{ffi_ty}", ffi_ty = ffi_ty);
1436        if arg.annotation.is_const_ptr() {
1437            return format!("*const {base}", base = base);
1438        }
1439        if arg.annotation.is_mut_ptr() {
1440            return format!("*mut {base}", base = base);
1441        }
1442        return base;
1443    }
1444
1445    rust_type_for(&arg.member_type)
1446}
1447
1448fn fn_signature_params(args: &[RecordMember], model: &ApiModel, receiver: Option<&str>) -> String {
1449    let mut parts = Vec::new();
1450    let callback_info_map = build_callback_info_map(model);
1451    let callback_fn_map = build_callback_function_map(model);
1452    let length_fields = length_field_names(args);
1453
1454    if let Some(recv) = receiver {
1455        if recv == "self" {
1456            parts.push("&self".to_string());
1457        }
1458    }
1459
1460    for arg in args {
1461        if let Some(callback_fn_name) = callback_info_map.get(&arg.member_type) {
1462            let callback_args = callback_fn_map
1463                .get(callback_fn_name)
1464                .map(|c| callback_arg_list(&c.def.args))
1465                .unwrap_or_else(|| "".to_string());
1466            parts.push(format!(
1467                r#"callback: impl FnMut({callback_args}) + Send + 'static"#,
1468                callback_args = callback_args
1469            ));
1470            continue;
1471        }
1472
1473        if length_fields.contains(&arg.name) {
1474            continue;
1475        }
1476
1477        let param_name = safe_ident(&snake_case_name(&arg.name));
1478        let param_ty = rust_param_type(arg);
1479        let needs_mut = arg.length.is_some() && arg.annotation.is_mut_ptr();
1480        let param_name = if needs_mut {
1481            format!(r#"mut {param_name}"#, param_name = param_name)
1482        } else {
1483            param_name
1484        };
1485        parts.push(format!(
1486            r#"{param_name}: {param_ty}"#,
1487            param_name = param_name,
1488            param_ty = param_ty
1489        ));
1490    }
1491
1492    parts.join(", ")
1493}
1494
1495fn rust_return_type(ret: &ReturnType) -> String {
1496    let base = if ret.get_type() == "void" {
1497        "()".to_string()
1498    } else {
1499        rust_type_for(ret.get_type())
1500    };
1501    if ret.is_optional() {
1502        format!(r#"Option<{base}>"#, base = base)
1503    } else {
1504        base
1505    }
1506}
1507
1508// args_usage_list removed: superseded by emit_ffi_arg_prelude.
1509
1510struct TypeIndex {
1511    objects: HashMap<String, ()>,
1512    enums: HashMap<String, ()>,
1513    bitmasks: HashMap<String, ()>,
1514    structs: HashMap<String, bool>,
1515    callback_infos: HashMap<String, ()>,
1516    structs_need_free: HashMap<String, ()>,
1517}
1518
1519impl TypeIndex {
1520    fn new(model: &ApiModel) -> Self {
1521        let mut objects = HashMap::new();
1522        let mut enums = HashMap::new();
1523        let mut bitmasks = HashMap::new();
1524        let mut structs = HashMap::new();
1525        let mut callback_infos = HashMap::new();
1526        let mut structs_need_free = HashMap::new();
1527
1528        for o in &model.objects {
1529            objects.insert(o.name.clone(), ());
1530        }
1531        for e in &model.enums {
1532            enums.insert(e.name.clone(), ());
1533        }
1534        for b in &model.bitmasks {
1535            bitmasks.insert(b.name.clone(), ());
1536        }
1537        for s in &model.structures {
1538            structs.insert(s.name.clone(), s.def.extensible.is_extensible());
1539            if struct_needs_free_members(s) {
1540                structs_need_free.insert(s.name.clone(), ());
1541            }
1542        }
1543        for c in &model.callback_infos {
1544            callback_infos.insert(c.name.clone(), ());
1545        }
1546
1547        Self {
1548            objects,
1549            enums,
1550            bitmasks,
1551            structs,
1552            callback_infos,
1553            structs_need_free,
1554        }
1555    }
1556
1557    fn is_object(&self, name: &str) -> bool {
1558        self.objects.contains_key(name)
1559    }
1560
1561    fn is_enum(&self, name: &str) -> bool {
1562        self.enums.contains_key(name)
1563    }
1564
1565    fn is_bitmask(&self, name: &str) -> bool {
1566        self.bitmasks.contains_key(name)
1567    }
1568
1569    fn struct_extensible(&self, name: &str) -> Option<bool> {
1570        self.structs.get(name).copied()
1571    }
1572
1573    fn is_callback_info(&self, name: &str) -> bool {
1574        self.callback_infos.contains_key(name)
1575    }
1576
1577    fn struct_needs_free_members(&self, name: &str) -> bool {
1578        self.structs_need_free.contains_key(name)
1579    }
1580}
1581
1582fn emit_ffi_arg_prelude(
1583    args: &[RecordMember],
1584    model: &ApiModel,
1585    index: &TypeIndex,
1586    c_prefix: &str,
1587) -> (String, Vec<String>, bool) {
1588    let mut prelude = Vec::new();
1589    let mut ffi_args = Vec::new();
1590    let mut has_callback = false;
1591    let callback_info_map = build_callback_info_map(model);
1592    let callback_info_mode_map = build_callback_info_mode_map(model);
1593    let mut length_data_map: HashMap<String, usize> = HashMap::new();
1594    for (idx, arg) in args.iter().enumerate() {
1595        if let Some(LengthValue::String(name)) = &arg.length {
1596            length_data_map.insert(name.clone(), idx);
1597        }
1598    }
1599
1600    for arg in args {
1601        if let Some(data_index) = length_data_map.get(&arg.name) {
1602            let data_arg = &args[*data_index];
1603            let data_name = safe_ident(&snake_case_name(&data_arg.name));
1604            let len_expr = length_expr_for_data_arg(data_arg, &data_name);
1605            let target_ty = rust_type_for(&arg.member_type);
1606            let len_expr = if target_ty == "usize" {
1607                len_expr
1608            } else {
1609                format!(
1610                    "({len_expr}) as {target_ty}",
1611                    len_expr = len_expr,
1612                    target_ty = target_ty
1613                )
1614            };
1615            ffi_args.push(len_expr);
1616            continue;
1617        }
1618        if index.is_callback_info(&arg.member_type) {
1619            if let Some(callback_fn_name) = callback_info_map.get(&arg.member_type) {
1620                let callback_ty = callback_type_name(callback_fn_name);
1621                let trampoline = callback_trampoline_name(callback_fn_name);
1622                let info_name = type_name(&arg.member_type);
1623                let name = safe_ident(&snake_case_name(&arg.name));
1624                let has_mode = callback_info_mode_map
1625                    .get(&arg.member_type)
1626                    .copied()
1627                    .unwrap_or(false);
1628                let mode_line = if has_mode {
1629                    format!(
1630                        "            mode: ffi::{prefix}CallbackMode_{prefix}CallbackMode_AllowSpontaneous,\n",
1631                        prefix = c_prefix
1632                    )
1633                } else {
1634                    String::new()
1635                };
1636
1637                prelude.push(format!(
1638                    r#"        let callback_box: {callback_ty} = Box::new(callback);"#,
1639                    callback_ty = callback_ty
1640                ));
1641                prelude.push(
1642                    r#"        let callback_box = Box::new(Some(callback_box));"#.to_string(),
1643                );
1644                prelude.push(
1645                    r#"        let callback_userdata = Box::into_raw(callback_box).cast::<std::ffi::c_void>();"#
1646                        .to_string(),
1647                );
1648                prelude.push(format!(
1649                    r#"        let {name}_ffi = ffi::{c_prefix}{info_name} {{
1650            nextInChain: std::ptr::null_mut(),
1651{mode_line}            callback: Some({trampoline}),
1652            userdata1: callback_userdata,
1653            userdata2: std::ptr::null_mut(),
1654        }};"#,
1655                    name = name,
1656                    info_name = info_name,
1657                    trampoline = trampoline,
1658                    mode_line = mode_line,
1659                    c_prefix = c_prefix
1660                ));
1661                ffi_args.push(format!("{name}_ffi", name = name));
1662            } else {
1663                has_callback = true;
1664                prelude.push("        let _ = callback;".to_string());
1665                ffi_args.push("std::ptr::null_mut()".to_string());
1666            }
1667            continue;
1668        }
1669
1670        let name = safe_ident(&snake_case_name(&arg.name));
1671        if arg.length.is_some() {
1672            if index.struct_extensible(&arg.member_type).is_some() {
1673                let ffi_ty = ffi_type_name(&arg.member_type, c_prefix);
1674                let ptr_expr = if arg.annotation.is_mut_ptr() {
1675                    format!("{name}_raw.as_mut_ptr()", name = name)
1676                } else {
1677                    format!("{name}_raw.as_ptr()", name = name)
1678                };
1679                let null_ptr = if arg.annotation.is_mut_ptr() {
1680                    "std::ptr::null_mut()"
1681                } else {
1682                    "std::ptr::null()"
1683                };
1684                if arg.optional {
1685                    prelude.push(format!(
1686                        r#"        let mut {name}_raw: Vec<ffi::{ffi_ty}> = Vec::new();
1687        let mut {name}_storage: Vec<ChainedStructStorage> = Vec::new();
1688        let {name}_ptr = if let Some(value) = {name}.as_deref() {{
1689            for item in value {{
1690                let (raw, storage) = item.to_ffi();
1691                {name}_raw.push(raw);
1692                {name}_storage.push(storage);
1693            }}
1694            {ptr_expr}
1695        }} else {{
1696            {null_ptr}
1697        }};"#,
1698                        name = name,
1699                        ffi_ty = ffi_ty,
1700                        ptr_expr = ptr_expr,
1701                        null_ptr = null_ptr
1702                    ));
1703                } else {
1704                    prelude.push(format!(
1705                        r#"        let mut {name}_raw: Vec<ffi::{ffi_ty}> = Vec::new();
1706        let mut {name}_storage: Vec<ChainedStructStorage> = Vec::new();
1707        for item in {name}.iter() {{
1708            let (raw, storage) = item.to_ffi();
1709            {name}_raw.push(raw);
1710            {name}_storage.push(storage);
1711        }}
1712        let {name}_ptr = {ptr_expr};"#,
1713                        name = name,
1714                        ffi_ty = ffi_ty,
1715                        ptr_expr = ptr_expr
1716                    ));
1717                }
1718                ffi_args.push(format!("{name}_ptr", name = name));
1719                continue;
1720            }
1721
1722            if index.is_object(&arg.member_type) {
1723                let obj = type_name(&arg.member_type);
1724                let ptr_expr = if arg.annotation.is_mut_ptr() {
1725                    format!("{name}_raw.as_mut_ptr()", name = name)
1726                } else {
1727                    format!("{name}_raw.as_ptr()", name = name)
1728                };
1729                let null_ptr = if arg.annotation.is_mut_ptr() {
1730                    "std::ptr::null_mut()"
1731                } else {
1732                    "std::ptr::null()"
1733                };
1734                if arg.optional {
1735                    prelude.push(format!(
1736                        r#"        let mut {name}_raw: Vec<ffi::{prefix}{obj}> = Vec::new();
1737        let {name}_ptr = if let Some(value) = {name}.as_deref() {{
1738            {name}_raw = value.iter().map(|v| v.as_raw()).collect();
1739            {ptr_expr}
1740        }} else {{
1741            {null_ptr}
1742        }};"#,
1743                        name = name,
1744                        obj = obj,
1745                        prefix = c_prefix,
1746                        ptr_expr = ptr_expr,
1747                        null_ptr = null_ptr
1748                    ));
1749                } else {
1750                    prelude.push(format!(
1751                        r#"        let mut {name}_raw: Vec<ffi::{prefix}{obj}> = {name}.iter().map(|v| v.as_raw()).collect();
1752        let {name}_ptr = {ptr_expr};"#,
1753                        name = name,
1754                        obj = obj,
1755                        prefix = c_prefix,
1756                        ptr_expr = ptr_expr
1757                    ));
1758                }
1759                ffi_args.push(format!("{name}_ptr", name = name));
1760                continue;
1761            }
1762
1763            let null_ptr = if arg.annotation.is_mut_ptr() {
1764                "std::ptr::null_mut()"
1765            } else {
1766                "std::ptr::null()"
1767            };
1768            if arg.optional {
1769                let deref = if arg.annotation.is_mut_ptr() {
1770                    "as_deref_mut()"
1771                } else {
1772                    "as_deref()"
1773                };
1774                let ptr_expr = if arg.annotation.is_mut_ptr() {
1775                    "value.as_mut_ptr()"
1776                } else {
1777                    "value.as_ptr()"
1778                };
1779                prelude.push(format!(
1780                    r#"        let {name}_ptr = if let Some(value) = {name}.{deref} {{
1781            {ptr_expr}
1782        }} else {{
1783            {null_ptr}
1784        }};"#,
1785                    name = name,
1786                    deref = deref,
1787                    ptr_expr = ptr_expr,
1788                    null_ptr = null_ptr
1789                ));
1790            } else {
1791                let ptr_expr = if arg.annotation.is_mut_ptr() {
1792                    format!("{name}.as_mut_ptr()", name = name)
1793                } else {
1794                    format!("{name}.as_ptr()", name = name)
1795                };
1796                prelude.push(format!(
1797                    r#"        let {name}_ptr = {ptr_expr};"#,
1798                    name = name,
1799                    ptr_expr = ptr_expr
1800                ));
1801            }
1802            ffi_args.push(format!("{name}_ptr", name = name));
1803            continue;
1804        }
1805        if index.is_object(&arg.member_type) {
1806            if arg.optional {
1807                prelude.push(format!(
1808                    r#"        let {name}_raw = {name}.as_ref().map(|v| v.as_raw()).unwrap_or(std::ptr::null_mut());"#,
1809                    name = name
1810                ));
1811                ffi_args.push(format!("{name}_raw", name = name));
1812            } else {
1813                ffi_args.push(format!("{name}.as_raw()", name = name));
1814            }
1815            continue;
1816        }
1817
1818        if index.is_enum(&arg.member_type) || index.is_bitmask(&arg.member_type) {
1819            let ffi_ty = ffi_type_name(&arg.member_type, c_prefix);
1820            prelude.push(format!(
1821                r#"        let {name}_ffi: ffi::{ffi_ty} = {name}.into();"#,
1822                name = name,
1823                ffi_ty = ffi_ty
1824            ));
1825            ffi_args.push(format!("{name}_ffi", name = name));
1826            continue;
1827        }
1828
1829        if arg.member_type == "string view" {
1830            if arg.optional {
1831                prelude.push(format!(
1832                    r#"        let {name}_ffi = if let Some(value) = &{name} {{
1833            ffi::{prefix}StringView {{ data: value.as_ptr().cast(), length: value.len() }}
1834        }} else {{
1835            ffi::{prefix}StringView {{ data: std::ptr::null(), length: 0 }}
1836        }};"#,
1837                    name = name,
1838                    prefix = c_prefix
1839                ));
1840            } else {
1841                prelude.push(format!(
1842                    r#"        let {name}_ffi = ffi::{prefix}StringView {{
1843            data: {name}.as_ptr().cast(),
1844            length: {name}.len(),
1845        }};"#,
1846                    name = name,
1847                    prefix = c_prefix
1848                ));
1849            }
1850            ffi_args.push(format!("{name}_ffi", name = name));
1851            continue;
1852        }
1853
1854        if arg.member_type == "bool" {
1855            prelude.push(format!(
1856                r#"        let {name}_ffi: ffi::{prefix}Bool = if {name} {{ 1 }} else {{ 0 }};"#,
1857                name = name,
1858                prefix = c_prefix
1859            ));
1860            ffi_args.push(format!("{name}_ffi", name = name));
1861            continue;
1862        }
1863
1864        if index.struct_extensible(&arg.member_type).is_some() {
1865            if arg.optional {
1866                if arg.annotation.is_const_ptr() || arg.annotation.is_mut_ptr() {
1867                    let (ptr_ctor, null_ptr) = if arg.annotation.is_mut_ptr() {
1868                        ("std::ptr::addr_of_mut!", "std::ptr::null_mut()")
1869                    } else {
1870                        ("std::ptr::addr_of!", "std::ptr::null()")
1871                    };
1872                    prelude.push(format!(
1873                        r#"        let mut {name}_storage = ChainedStructStorage::new();
1874        let {name}_ptr = if let Some(value) = &{name} {{
1875            let ({name}_ffi, storage) = value.to_ffi();
1876            {name}_storage = storage;
1877            {ptr_ctor}({name}_ffi)
1878        }} else {{
1879            {null_ptr}
1880        }};"#,
1881                        name = name,
1882                        ptr_ctor = ptr_ctor,
1883                        null_ptr = null_ptr
1884                    ));
1885                    ffi_args.push(format!("{name}_ptr", name = name));
1886                    continue;
1887                }
1888                prelude.push(format!(r#"        let _ = {name};"#, name = name));
1889                ffi_args.push("std::ptr::null()".to_string());
1890                continue;
1891            }
1892
1893            if arg.annotation.is_const_ptr() {
1894                prelude.push(format!(
1895                    r#"        let ({name}_ffi, _{name}_storage) = {name}.to_ffi();"#,
1896                    name = name
1897                ));
1898                prelude.push(format!(
1899                    r#"        let {name}_ptr = std::ptr::addr_of!({name}_ffi);"#,
1900                    name = name
1901                ));
1902                ffi_args.push(format!("{name}_ptr", name = name));
1903                continue;
1904            }
1905
1906            if arg.annotation.is_mut_ptr() {
1907                prelude.push(format!(
1908                    r#"        let (mut {name}_ffi, _{name}_storage) = {name}.to_ffi();"#,
1909                    name = name
1910                ));
1911                prelude.push(format!(
1912                    r#"        let {name}_ptr = std::ptr::addr_of_mut!({name}_ffi);"#,
1913                    name = name
1914                ));
1915                ffi_args.push(format!("{name}_ptr", name = name));
1916                continue;
1917            }
1918
1919            prelude.push(format!(
1920                r#"        let ({name}_ffi, _{name}_storage) = {name}.to_ffi();"#,
1921                name = name
1922            ));
1923            ffi_args.push(format!("{name}_ffi", name = name));
1924            continue;
1925        }
1926
1927        ffi_args.push(name);
1928    }
1929
1930    (prelude.join("\n"), ffi_args, has_callback)
1931}
1932
1933fn emit_out_struct_postlude(args: &[RecordMember], index: &TypeIndex) -> String {
1934    let mut lines = Vec::new();
1935    for arg in args {
1936        if arg.optional {
1937            continue;
1938        }
1939        if !arg.annotation.is_mut_ptr() {
1940            continue;
1941        }
1942        if index.struct_extensible(&arg.member_type).is_none() {
1943            continue;
1944        }
1945        let name = safe_ident(&snake_case_name(&arg.name));
1946        let ty = type_name(&arg.member_type);
1947        lines.push(format!(
1948            r#"        *{name} = {ty}::from_ffi({name}_ffi);"#,
1949            name = name,
1950            ty = ty
1951        ));
1952        let _ = index.struct_needs_free_members(&arg.member_type);
1953    }
1954    lines.join("\n")
1955}
1956
1957fn emit_return_conversion(ret: Option<&ReturnType>, index: &TypeIndex, name: &str) -> String {
1958    let Some(ret) = ret else {
1959        return "        let _ = ();".to_string();
1960    };
1961
1962    let ty = ret.get_type();
1963    if index.is_object(ty) {
1964        let obj = type_name(ty);
1965        if ret.is_optional() {
1966            return format!(
1967                r#"        if {name}.is_null() {{
1968            None
1969        }} else {{
1970            Some(unsafe {{ {obj}::from_raw({name}) }})
1971        }}"#,
1972                name = name,
1973                obj = obj
1974            );
1975        }
1976        return format!(
1977            r#"        unsafe {{ {obj}::from_raw({name}) }}"#,
1978            name = name,
1979            obj = obj
1980        );
1981    }
1982
1983    if index.is_enum(ty) || index.is_bitmask(ty) {
1984        return format!(r#"        {name}.into()"#, name = name);
1985    }
1986
1987    if index.struct_extensible(ty).is_some() {
1988        return format!(
1989            r#"        {ty}::from_ffi({name})"#,
1990            ty = type_name(ty),
1991            name = name
1992        );
1993    }
1994
1995    if ret.get_type() == "void" {
1996        return "        let _ = ();".to_string();
1997    }
1998
1999    if ret.get_type() == "bool" {
2000        return format!(r#"        {name} != 0"#, name = name);
2001    }
2002
2003    format!(r#"        {name}"#, name = name)
2004}
2005
2006fn indent_block(text: &str, spaces: usize) -> String {
2007    if text.is_empty() {
2008        return String::new();
2009    }
2010    let pad = " ".repeat(spaces);
2011    text.lines()
2012        .map(|line| format!("{pad}{line}", pad = pad, line = line))
2013        .collect::<Vec<_>>()
2014        .join("\n")
2015}
2016
2017fn rust_param_type(arg: &RecordMember) -> String {
2018    let base = rust_type_for(&arg.member_type);
2019
2020    let mut ty = if arg.length.is_some() {
2021        if arg.annotation.is_mut_ptr() {
2022            format!(r#"&mut [{base}]"#, base = base)
2023        } else {
2024            format!(r#"&[{base}]"#, base = base)
2025        }
2026    } else if arg.member_type.contains('*') {
2027        base
2028    } else if arg.annotation.is_const_ptr() {
2029        format!(r#"&{base}"#, base = base)
2030    } else if arg.annotation.is_mut_ptr() {
2031        format!(r#"&mut {base}"#, base = base)
2032    } else {
2033        base
2034    };
2035
2036    if arg.optional {
2037        ty = format!(r#"Option<{ty}>"#, ty = ty);
2038    }
2039
2040    ty
2041}
2042
2043fn callback_param_type(arg: &RecordMember) -> String {
2044    let base = rust_type_for(&arg.member_type);
2045
2046    let mut ty = if arg.length.is_some() {
2047        format!(r#"Vec<{base}>"#, base = base)
2048    } else if arg.member_type.contains('*') {
2049        base
2050    } else if arg.annotation.is_const_ptr() {
2051        format!(r#"&{base}"#, base = base)
2052    } else if arg.annotation.is_mut_ptr() {
2053        format!(r#"&mut {base}"#, base = base)
2054    } else {
2055        base
2056    };
2057
2058    if arg.optional {
2059        ty = format!(r#"Option<{ty}>"#, ty = ty);
2060    }
2061
2062    ty
2063}
2064
2065fn struct_field_type(member: &RecordMember, index: &TypeIndex) -> String {
2066    if is_char_string_list(member) {
2067        return "Option<Vec<String>>".to_string();
2068    }
2069
2070    let base = rust_type_for(&member.member_type);
2071    let is_struct = index.struct_extensible(&member.member_type).is_some()
2072        || index.is_callback_info(&member.member_type);
2073
2074    let ty = if member.length.is_some() {
2075        format!(r#"Vec<{base}>"#, base = base)
2076    } else if member.member_type.contains('*') {
2077        base
2078    } else if member.annotation.is_const_ptr() && !is_struct {
2079        format!(r#"*const {base}"#, base = base)
2080    } else if member.annotation.is_mut_ptr() && !is_struct {
2081        format!(r#"*mut {base}"#, base = base)
2082    } else {
2083        base
2084    };
2085
2086    format!(r#"Option<{ty}>"#, ty = ty)
2087}
2088
2089fn builder_param_type(member: &RecordMember, index: &TypeIndex) -> String {
2090    if is_char_string_list(member) {
2091        return "Vec<String>".to_string();
2092    }
2093
2094    let base = rust_type_for(&member.member_type);
2095    let is_struct = index.struct_extensible(&member.member_type).is_some()
2096        || index.is_callback_info(&member.member_type);
2097
2098    if member.length.is_some() {
2099        format!(r#"Vec<{base}>"#, base = base)
2100    } else if member.member_type.contains('*') {
2101        base
2102    } else if member.annotation.is_const_ptr() && !is_struct {
2103        format!(r#"*const {base}"#, base = base)
2104    } else if member.annotation.is_mut_ptr() && !is_struct {
2105        format!(r#"*mut {base}"#, base = base)
2106    } else {
2107        base
2108    }
2109}
2110
2111fn member_default_expr(
2112    member: &RecordMember,
2113    index: &TypeIndex,
2114    c_prefix: &str,
2115    constant_map: &HashMap<String, String>,
2116) -> Option<String> {
2117    if member.no_default == Some(true) {
2118        return None;
2119    }
2120    let default_value = member.default.as_ref()?;
2121    let ty = &member.member_type;
2122
2123    let numeric_expr = |value: u64| -> String {
2124        if ty == "bool" {
2125            if value == 0 {
2126                "false".to_string()
2127            } else {
2128                "true".to_string()
2129            }
2130        } else if index.is_enum(ty) {
2131            let ffi_ty = ffi_type_name(ty, c_prefix);
2132            format!(
2133                "{enum_ty}::from({value} as ffi::{ffi_ty})",
2134                enum_ty = type_name(ty)
2135            )
2136        } else if index.is_bitmask(ty) {
2137            format!(
2138                "{mask_ty}::from_bits_truncate({value} as u64)",
2139                mask_ty = type_name(ty)
2140            )
2141        } else {
2142            value.to_string()
2143        }
2144    };
2145
2146    match default_value {
2147        Value::Bool(value) => Some(value.to_string()),
2148        Value::Number(num) => num.as_u64().map(numeric_expr),
2149        Value::String(s) => {
2150            let lowered = s.trim().to_ascii_lowercase();
2151            if lowered == "nullptr" || lowered == "null" {
2152                return None;
2153            }
2154            if let Some(num) = parse_numeric_string_u64(s) {
2155                Some(numeric_expr(num))
2156            } else if index.is_enum(ty) {
2157                Some(format!(
2158                    r#"{}::{}"#,
2159                    type_name(ty),
2160                    enum_variant_name_camel(s)
2161                ))
2162            } else if index.is_bitmask(ty) {
2163                Some(format!(r#"{}::{}"#, type_name(ty), bitmask_variant_name(s)))
2164            } else if let Some(const_name) = constant_map.get(s) {
2165                Some(const_name.clone())
2166            } else {
2167                None
2168            }
2169        }
2170        _ => None,
2171    }
2172}
2173
2174fn rust_type_for(name: &str) -> String {
2175    match name {
2176        "void" => "std::ffi::c_void".to_string(),
2177        "void *" => "*mut std::ffi::c_void".to_string(),
2178        "void const *" => "*const std::ffi::c_void".to_string(),
2179        "bool" => "bool".to_string(),
2180        "int" => "i32".to_string(),
2181        "uint" => "u32".to_string(),
2182        "uint8_t" => "u8".to_string(),
2183        "uint16_t" => "u16".to_string(),
2184        "uint32_t" => "u32".to_string(),
2185        "uint64_t" => "u64".to_string(),
2186        "int8_t" => "i8".to_string(),
2187        "int16_t" => "i16".to_string(),
2188        "int32_t" => "i32".to_string(),
2189        "int64_t" => "i64".to_string(),
2190        "size_t" => "usize".to_string(),
2191        "float" => "f32".to_string(),
2192        "double" => "f64".to_string(),
2193        "char" => "std::os::raw::c_char".to_string(),
2194        "string view" => "String".to_string(),
2195        other => type_name(other),
2196    }
2197}
2198
2199fn type_name(name: &str) -> String {
2200    let raw = camel_case_with_acronyms(name);
2201    normalize_digit_prefix_camel(&raw)
2202}
2203
2204fn enum_variant_name_camel(name: &str) -> String {
2205    let raw = camel_case_with_acronyms(name);
2206    normalize_digit_prefix_camel(&raw)
2207}
2208
2209fn ffi_enum_const_name(ffi_type: &str, variant: &str) -> String {
2210    format!("{ffi_type}_{ffi_type}_{variant}")
2211}
2212
2213fn ffi_bitmask_const_name(ffi_type: &str, variant: &str) -> String {
2214    format!("{ffi_type}_{variant}")
2215}
2216
2217fn ffi_enum_value_name(name: &str) -> String {
2218    if name.trim().eq_ignore_ascii_case("srgb") {
2219        return "SRGB".to_string();
2220    }
2221    let mut out = String::new();
2222    for word in name.split_whitespace() {
2223        let mut parts = Vec::new();
2224        for part in word.split('-') {
2225            if part.is_empty() {
2226                continue;
2227            }
2228            let value = if part.chars().all(|c| c.is_ascii_digit()) {
2229                part.to_string()
2230            } else if part
2231                .chars()
2232                .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit())
2233            {
2234                part.to_string()
2235            } else {
2236                let mut chars = part.chars();
2237                let mut s = String::new();
2238                if let Some(first) = chars.next() {
2239                    s.push(first.to_ascii_uppercase());
2240                    s.push_str(&chars.as_str().to_ascii_lowercase());
2241                }
2242                s
2243            };
2244            parts.push(value);
2245        }
2246        out.push_str(&parts.join("_"));
2247    }
2248    for (from, to) in acronym_fixes() {
2249        out = out.replace(from, to);
2250    }
2251    let extra_fixes = [
2252        ("Opengles", "OpenGLES"),
2253        ("Opengl", "OpenGL"),
2254        ("Gpu", "GPU"),
2255        ("Cpu", "CPU"),
2256        ("Winui", "WinUI"),
2257    ];
2258    for (from, to) in extra_fixes {
2259        out = out.replace(from, to);
2260    }
2261    out
2262}
2263
2264fn bitmask_variant_name(name: &str) -> String {
2265    let raw = shouty_snake_case_name(name);
2266    if let Some(first) = raw.chars().next() {
2267        if first.is_ascii_digit() {
2268            return normalize_digit_prefix(&raw);
2269        }
2270    }
2271    raw
2272}
2273
2274fn safe_ident(name: &str) -> String {
2275    let keywords = [
2276        "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn",
2277        "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref",
2278        "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe",
2279        "use", "where", "while", "async", "await", "dyn",
2280    ];
2281
2282    if keywords.contains(&name) {
2283        format!(r#"r#{name}"#, name = name)
2284    } else {
2285        name.to_string()
2286    }
2287}
2288
2289fn parse_numeric_string(value: &str) -> Option<u32> {
2290    let trimmed = value.trim();
2291    if let Some(hex) = trimmed.strip_prefix("0x") {
2292        u32::from_str_radix(hex, 16).ok()
2293    } else if let Some(hex) = trimmed.strip_prefix("0X") {
2294        u32::from_str_radix(hex, 16).ok()
2295    } else {
2296        trimmed.parse::<u32>().ok()
2297    }
2298}
2299
2300fn normalize_digit_prefix_camel(value: &str) -> String {
2301    let mut chars = value.chars();
2302    let Some(first) = chars.next() else {
2303        return value.to_string();
2304    };
2305    if !first.is_ascii_digit() {
2306        return value.to_string();
2307    }
2308    let Some(second) = chars.next() else {
2309        return value.to_string();
2310    };
2311    let mut rest: String = chars.collect();
2312    rest.insert(0, first);
2313    let mut out = String::new();
2314    out.push(second.to_ascii_uppercase());
2315    out.push_str(&rest);
2316    out
2317}
2318
2319fn normalize_digit_prefix(raw: &str) -> String {
2320    let mut chars = raw.chars().peekable();
2321    let mut digits = String::new();
2322    while let Some(c) = chars.peek() {
2323        if c.is_ascii_digit() {
2324            digits.push(*c);
2325            chars.next();
2326        } else {
2327            break;
2328        }
2329    }
2330
2331    let rest: String = chars.collect();
2332    if let Some(stripped) = rest.strip_prefix('D') {
2333        format!("D{digits}{stripped}", digits = digits, stripped = stripped)
2334    } else {
2335        format!("D{digits}{rest}", digits = digits, rest = rest)
2336    }
2337}
2338
2339fn normalize_digit_prefix_snake(raw: &str) -> String {
2340    let mut chars = raw.chars();
2341    let Some(first) = chars.next() else {
2342        return raw.to_string();
2343    };
2344    if !first.is_ascii_digit() {
2345        return raw.to_string();
2346    }
2347    let Some(second) = chars.next() else {
2348        return raw.to_string();
2349    };
2350    let mut rest: String = chars.collect();
2351    rest.insert(0, first);
2352    let mut out = String::new();
2353    out.push(second);
2354    out.push_str(&rest);
2355    out
2356}
2357
2358fn uppercase_after_digits(value: &str) -> String {
2359    let mut out = String::with_capacity(value.len());
2360    let mut prev_digit = false;
2361    for ch in value.chars() {
2362        if prev_digit && ch.is_ascii_lowercase() {
2363            out.push(ch.to_ascii_uppercase());
2364        } else {
2365            out.push(ch);
2366        }
2367        prev_digit = ch.is_ascii_digit();
2368    }
2369    out
2370}
2371
2372fn ffi_type_name(canonical: &str, c_prefix: &str) -> String {
2373    if canonical == "INTERNAL_HAVE_EMDAWNWEBGPU_HEADER" {
2374        return format!("{c_prefix}{canonical}");
2375    }
2376    format!("{}{}", c_prefix, camel_case_with_acronyms(canonical))
2377}
2378
2379fn ffi_fn_name(canonical: &str, c_prefix: &str) -> String {
2380    format!(
2381        "{}{}",
2382        c_prefix.to_lowercase(),
2383        camel_case_with_acronyms(canonical)
2384    )
2385}
2386
2387fn camel_case_name(name: &str) -> String {
2388    name.to_upper_camel_case()
2389}
2390
2391fn snake_case_name(name: &str) -> String {
2392    let raw = name.to_snake_case();
2393    normalize_digit_prefix_snake(&raw)
2394}
2395
2396fn shouty_snake_case_name(name: &str) -> String {
2397    let raw = name.to_shouty_snake_case();
2398    if let Some(first) = raw.chars().next() {
2399        if first.is_ascii_digit() {
2400            return normalize_digit_prefix(&raw);
2401        }
2402    }
2403    raw
2404}
2405
2406fn camel_case_with_acronyms(name: &str) -> String {
2407    let mut out = camel_case_name(name);
2408    for (from, to) in acronym_fixes() {
2409        out = out.replace(from, to);
2410    }
2411    uppercase_after_digits(&out)
2412}
2413
2414fn acronym_fixes() -> &'static [(&'static str, &'static str)] {
2415    &[
2416        ("WebGpu", "WebGPU"),
2417        ("WebXr", "WebXR"),
2418        ("Webgpu", "WebGPU"),
2419        ("Webxr", "WebXR"),
2420        ("Wgpu", "WGPU"),
2421        ("Wgsl", "WGSL"),
2422        ("Hlsl", "HLSL"),
2423        ("Html", "HTML"),
2424        ("Spirv", "SPIRV"),
2425        ("Dxgi", "DXGI"),
2426        ("Mtl", "MTL"),
2427        ("IoSurface", "IOSurface"),
2428        ("Hwnd", "HWND"),
2429        ("Uwp", "UWP"),
2430        ("WinUi", "WinUI"),
2431        ("Oom", "OOM"),
2432        ("Fd", "FD"),
2433        ("D3d12", "D3D12"),
2434        ("D3d11", "D3D11"),
2435        ("D3d", "D3D"),
2436        ("Vk", "Vk"),
2437        ("Xcb", "XCB"),
2438        ("OpenGl", "OpenGL"),
2439        ("OpenGles", "OpenGLES"),
2440        ("Egl", "EGL"),
2441    ]
2442}
2443
2444fn build_callback_info_map(model: &ApiModel) -> HashMap<String, String> {
2445    let mut map = HashMap::new();
2446    for info in &model.callback_infos {
2447        if let Some(callback_member) = info
2448            .def
2449            .members
2450            .iter()
2451            .find(|member| member.name == "callback")
2452        {
2453            map.insert(info.name.clone(), callback_member.member_type.clone());
2454        }
2455    }
2456    map
2457}
2458
2459fn build_callback_function_map(model: &ApiModel) -> HashMap<String, CallbackFunctionModel> {
2460    let mut map = HashMap::new();
2461    for callback in &model.callback_functions {
2462        map.insert(callback.name.clone(), callback.clone());
2463    }
2464    map
2465}
2466
2467fn build_callback_info_mode_map(model: &ApiModel) -> HashMap<String, bool> {
2468    let mut map = HashMap::new();
2469    for info in &model.callback_infos {
2470        let has_mode = info.def.members.iter().any(|member| member.name == "mode");
2471        map.insert(info.name.clone(), has_mode);
2472    }
2473    map
2474}
2475
2476fn build_stype_map(model: &ApiModel, c_prefix: &str) -> HashMap<String, String> {
2477    let mut map = HashMap::new();
2478    let stype_enum_name = "s type";
2479    let enum_name = type_name(stype_enum_name);
2480    if let Some(stype_enum) = model.enums.iter().find(|e| e.name == stype_enum_name) {
2481        for value in &stype_enum.def.values {
2482            let variant = enum_variant_name_camel(&value.name);
2483            map.insert(value.name.clone(), format!(r#"{enum_name}::{variant}"#));
2484        }
2485    }
2486    let _ = c_prefix;
2487    map
2488}
2489
2490fn length_value_expr(length: &LengthValue) -> String {
2491    match length {
2492        LengthValue::Number(value) => value.to_string(),
2493        LengthValue::String(name) => safe_ident(&snake_case_name(name)),
2494    }
2495}
2496
2497fn is_char_string_list(member: &RecordMember) -> bool {
2498    member.member_type == "char"
2499        && member.length.is_some()
2500        && member.annotation.is_const_const_ptr()
2501}
2502
2503fn struct_needs_free_members(s: &StructureModel) -> bool {
2504    let is_out = s.def.extensible.is_output()
2505        || s.def.chained.as_deref() == Some("out")
2506        || s.def.out == Some(true);
2507    if !is_out {
2508        return false;
2509    }
2510    s.def.members.iter().any(|member| {
2511        if member.member_type == "string view" || is_char_string_list(member) {
2512            return true;
2513        }
2514        if member.length.is_some() {
2515            return true;
2516        }
2517        !member.annotation.is_value()
2518    })
2519}
2520
2521fn ffi_field_name(name: &str) -> String {
2522    if !name.contains(' ') && name.chars().any(|c| c.is_ascii_uppercase()) {
2523        return name.to_string();
2524    }
2525    let parts: Vec<&str> = name.split_whitespace().collect();
2526    if parts.is_empty() {
2527        return String::new();
2528    }
2529    let mut out = if parts[0].len() == 1 && parts[0].chars().all(|c| c.is_ascii_uppercase()) {
2530        parts[0].to_string()
2531    } else {
2532        parts[0].to_lowercase()
2533    };
2534    for part in parts.iter().skip(1) {
2535        if part.eq_ignore_ascii_case("id") {
2536            out.push_str("ID");
2537            continue;
2538        }
2539        if part.len() == 1 {
2540            out.push_str(&part.to_ascii_uppercase());
2541            continue;
2542        }
2543        let mut chars = part.chars();
2544        if let Some(first) = chars.next() {
2545            out.push(first.to_ascii_uppercase());
2546            out.extend(chars);
2547        }
2548    }
2549    if out == "type" {
2550        "type_".to_string()
2551    } else {
2552        out
2553    }
2554}
2555
2556fn ffi_length_expr(length: &LengthValue) -> String {
2557    match length {
2558        LengthValue::Number(value) => format!("{value}usize", value = value),
2559        LengthValue::String(name) => format!("value.{} as usize", ffi_field_name(name)),
2560    }
2561}
2562
2563fn free_members_fn_name(struct_name: &str) -> String {
2564    format!("wgpu{}FreeMembers", type_name(struct_name))
2565}
2566
2567fn emit_struct_from_ffi_body(s: &StructureModel, index: &TypeIndex) -> String {
2568    let mut fields = Vec::new();
2569    let length_fields = length_field_names(&s.def.members);
2570
2571    if s.def.extensible.is_extensible() {
2572        fields.push("extensions: Vec::new(),".to_string());
2573    }
2574
2575    for member in &s.def.members {
2576        if length_fields.contains(&member.name) {
2577            continue;
2578        }
2579        let field_name = safe_ident(&snake_case_name(&member.name));
2580        let ffi_field = ffi_field_name(&member.name);
2581        let value_expr = if let Some(length) = &member.length {
2582            let len_expr = ffi_length_expr(length);
2583            if is_char_string_list(member) {
2584                format!(
2585                    r#"if value.{ffi_field}.is_null() {{
2586            None
2587        }} else {{
2588            Some(
2589                unsafe {{ std::slice::from_raw_parts(value.{ffi_field}, {len_expr}) }}
2590                    .iter()
2591                    .map(|raw| {{
2592                        if raw.is_null() {{
2593                            String::new()
2594                        }} else {{
2595                            unsafe {{ CStr::from_ptr(*raw) }}.to_string_lossy().into_owned()
2596                        }}
2597                    }})
2598                    .collect(),
2599            )
2600        }}"#,
2601                    ffi_field = ffi_field,
2602                    len_expr = len_expr
2603                )
2604            } else if index.is_object(&member.member_type) {
2605                let obj = type_name(&member.member_type);
2606                format!(
2607                    r#"if value.{ffi_field}.is_null() {{
2608            None
2609        }} else {{
2610            Some(
2611                unsafe {{ std::slice::from_raw_parts(value.{ffi_field}, {len_expr}) }}
2612                    .iter()
2613                    .map(|raw| unsafe {{ {obj}::from_raw(*raw) }})
2614                    .collect(),
2615            )
2616        }}"#,
2617                    ffi_field = ffi_field,
2618                    len_expr = len_expr,
2619                    obj = obj
2620                )
2621            } else if index.is_enum(&member.member_type) || index.is_bitmask(&member.member_type) {
2622                let rust_ty = type_name(&member.member_type);
2623                format!(
2624                    r#"if value.{ffi_field}.is_null() {{
2625            None
2626        }} else {{
2627            Some(
2628                unsafe {{ std::slice::from_raw_parts(value.{ffi_field}, {len_expr}) }}
2629                    .iter()
2630                    .map(|raw| {rust_ty}::from(*raw))
2631                    .collect(),
2632            )
2633        }}"#,
2634                    ffi_field = ffi_field,
2635                    len_expr = len_expr,
2636                    rust_ty = rust_ty
2637                )
2638            } else if index.struct_extensible(&member.member_type).is_some() {
2639                let rust_ty = type_name(&member.member_type);
2640                format!(
2641                    r#"if value.{ffi_field}.is_null() {{
2642            None
2643        }} else {{
2644            Some(
2645                unsafe {{ std::slice::from_raw_parts(value.{ffi_field}, {len_expr}) }}
2646                    .iter()
2647                    .map(|raw| {rust_ty}::from_ffi(*raw))
2648                    .collect(),
2649            )
2650        }}"#,
2651                    ffi_field = ffi_field,
2652                    len_expr = len_expr,
2653                    rust_ty = rust_ty
2654                )
2655            } else {
2656                format!(
2657                    r#"if value.{ffi_field}.is_null() {{
2658            None
2659        }} else {{
2660            Some(unsafe {{ std::slice::from_raw_parts(value.{ffi_field}, {len_expr}) }}.to_vec())
2661        }}"#,
2662                    ffi_field = ffi_field,
2663                    len_expr = len_expr
2664                )
2665            }
2666        } else if member.member_type == "string view" {
2667            if member.optional {
2668                format!(
2669                    r#"if value.{ffi_field}.data.is_null() || value.{ffi_field}.length == 0 {{
2670            None
2671        }} else {{
2672            Some(string_view_to_string(value.{ffi_field}))
2673        }}"#,
2674                    ffi_field = ffi_field
2675                )
2676            } else {
2677                format!(
2678                    "Some(string_view_to_string(value.{ffi_field}))",
2679                    ffi_field = ffi_field
2680                )
2681            }
2682        } else if index.is_enum(&member.member_type) || index.is_bitmask(&member.member_type) {
2683            format!("Some(value.{ffi_field}.into())", ffi_field = ffi_field)
2684        } else if index.is_callback_info(&member.member_type) {
2685            "None".to_string()
2686        } else if index.is_object(&member.member_type) {
2687            let obj = type_name(&member.member_type);
2688            if member.optional {
2689                format!(
2690                    r#"if value.{ffi_field}.is_null() {{
2691            None
2692        }} else {{
2693            Some(unsafe {{ {obj}::from_raw(value.{ffi_field}) }})
2694        }}"#,
2695                    ffi_field = ffi_field,
2696                    obj = obj
2697                )
2698            } else {
2699                format!(
2700                    r#"Some(unsafe {{ {obj}::from_raw(value.{ffi_field}) }})"#,
2701                    ffi_field = ffi_field,
2702                    obj = obj
2703                )
2704            }
2705        } else if index.struct_extensible(&member.member_type).is_some()
2706            && member.annotation.is_value()
2707        {
2708            let rust_ty = type_name(&member.member_type);
2709            format!(
2710                r#"Some({rust_ty}::from_ffi(value.{ffi_field}))"#,
2711                rust_ty = rust_ty,
2712                ffi_field = ffi_field
2713            )
2714        } else if index.struct_extensible(&member.member_type).is_some()
2715            && (member.annotation.is_const_ptr() || member.annotation.is_mut_ptr())
2716        {
2717            let rust_ty = type_name(&member.member_type);
2718            format!(
2719                r#"if value.{ffi_field}.is_null() {{
2720            None
2721        }} else {{
2722            Some({rust_ty}::from_ffi(unsafe {{ *value.{ffi_field} }}))
2723        }}"#,
2724                ffi_field = ffi_field,
2725                rust_ty = rust_ty
2726            )
2727        } else if member.member_type == "bool" {
2728            format!("Some(value.{ffi_field} != 0)", ffi_field = ffi_field)
2729        } else {
2730            format!("Some(value.{ffi_field})", ffi_field = ffi_field)
2731        };
2732
2733        fields.push(format!(
2734            r#"    {field_name}: {value_expr},"#,
2735            field_name = field_name,
2736            value_expr = value_expr
2737        ));
2738    }
2739
2740    if index.struct_needs_free_members(&s.name) {
2741        fields.push("    _free_members: Some(value),".to_string());
2742    }
2743
2744    if fields.is_empty() {
2745        return String::new();
2746    }
2747
2748    let fields_block = fields.join("\n");
2749    format!(
2750        r#"Self {{
2751{fields}
2752        }}"#,
2753        fields = fields_block
2754    )
2755}
2756
2757fn length_field_names(members: &[RecordMember]) -> HashSet<String> {
2758    let mut fields = HashSet::new();
2759    for member in members {
2760        if let Some(LengthValue::String(name)) = &member.length {
2761            fields.insert(name.clone());
2762        }
2763    }
2764    fields
2765}
2766
2767fn length_expr_for_data_arg(arg: &RecordMember, data_name: &str) -> String {
2768    if arg.optional {
2769        format!(
2770            r#"{data}.as_deref().map(|v| v.len()).unwrap_or(0)"#,
2771            data = data_name
2772        )
2773    } else {
2774        format!(r#"{data}.len()"#, data = data_name)
2775    }
2776}
2777
2778fn emit_struct_to_ffi_body(
2779    s: &StructureModel,
2780    index: &TypeIndex,
2781    c_prefix: &str,
2782    is_extensible: bool,
2783    callback_info_map: &HashMap<String, String>,
2784    callback_info_mode_map: &HashMap<String, bool>,
2785) -> String {
2786    let mut lines = Vec::new();
2787    let mut length_map: HashMap<String, String> = HashMap::new();
2788    let mut length_types: HashMap<String, String> = HashMap::new();
2789    for member in &s.def.members {
2790        length_types.insert(member.name.clone(), rust_type_for(&member.member_type));
2791        if let Some(LengthValue::String(name)) = &member.length {
2792            length_map.insert(name.clone(), member.name.clone());
2793        }
2794    }
2795
2796    lines.push(format!(
2797        r#"let mut raw: ffi::{ffi_ty} = unsafe {{ std::mem::zeroed() }};"#,
2798        ffi_ty = ffi_type_name(&s.name, c_prefix)
2799    ));
2800    if is_extensible {
2801        lines.push("raw.nextInChain = next;".to_string());
2802    }
2803
2804    for member in &s.def.members {
2805        let field_name = safe_ident(&snake_case_name(&member.name));
2806        let ffi_field = ffi_field_name(&member.name);
2807        if length_map.contains_key(&member.name) {
2808            let data_name = length_map.get(&member.name).cloned().unwrap();
2809            let data_ident = safe_ident(&snake_case_name(&data_name));
2810            let len_expr = format!(
2811                r#"self.{data}.as_ref().map(|v| v.len()).unwrap_or(0)"#,
2812                data = data_ident
2813            );
2814            let target_ty = rust_type_for(&member.member_type);
2815            let len_expr = if target_ty == "usize" {
2816                len_expr
2817            } else {
2818                format!("({len}) as {ty}", len = len_expr, ty = target_ty)
2819            };
2820            lines.push(format!(
2821                r#"raw.{ffi_field} = {len_expr};"#,
2822                ffi_field = ffi_field,
2823                len_expr = len_expr
2824            ));
2825            continue;
2826        }
2827
2828        if member.length.is_some() {
2829            let len_field = if let Some(LengthValue::String(name)) = &member.length {
2830                Some((name.clone(), ffi_field_name(name)))
2831            } else {
2832                None
2833            };
2834            let len_assign = len_field.as_ref().map(|(orig, field)| {
2835                let len_ty = length_types
2836                    .get(orig)
2837                    .cloned()
2838                    .unwrap_or_else(|| "usize".to_string());
2839                let len_expr = if len_ty == "usize" {
2840                    "len_value".to_string()
2841                } else {
2842                    format!("len_value as {len_ty}", len_ty = len_ty)
2843                };
2844                format!(
2845                    r#"raw.{field} = {len_expr};"#,
2846                    field = field,
2847                    len_expr = len_expr
2848                )
2849            });
2850
2851            let mut body = Vec::new();
2852            body.push("let len_value = values.len();".to_string());
2853
2854            if is_char_string_list(member) {
2855                body.push(
2856                    "let mut c_strings: Vec<std::ffi::CString> = Vec::with_capacity(values.len());"
2857                        .to_string(),
2858                );
2859                body.push("let mut ptrs: Vec<*const std::os::raw::c_char> = Vec::with_capacity(values.len());".to_string());
2860                body.push("for item in values.iter() {".to_string());
2861                body.push("    let c_string = std::ffi::CString::new(item.as_str()).unwrap_or_else(|_| std::ffi::CString::new(\"\").unwrap());".to_string());
2862                body.push("    ptrs.push(c_string.as_ptr());".to_string());
2863                body.push("    c_strings.push(c_string);".to_string());
2864                body.push("}".to_string());
2865                body.push("let ptr = storage.push_vec(ptrs);".to_string());
2866                body.push("storage.push_any(c_strings);".to_string());
2867                body.push(format!(r#"raw.{ffi_field} = ptr;"#, ffi_field = ffi_field));
2868                if let Some(assign) = len_assign {
2869                    body.push(assign);
2870                }
2871            } else if index.is_object(&member.member_type) {
2872                body.push(format!(
2873                    r#"let raw_vec: Vec<ffi::{prefix}{obj}> = values.iter().map(|v| v.as_raw()).collect();"#,
2874                    prefix = c_prefix,
2875                    obj = type_name(&member.member_type)
2876                ));
2877                body.push("let ptr = storage.push_vec(raw_vec);".to_string());
2878                body.push(format!(r#"raw.{ffi_field} = ptr;"#, ffi_field = ffi_field));
2879                if let Some(assign) = len_assign {
2880                    body.push(assign);
2881                }
2882            } else if index.is_enum(&member.member_type) || index.is_bitmask(&member.member_type) {
2883                let ffi_ty = ffi_type_name(&member.member_type, c_prefix);
2884                body.push(format!(
2885                    r#"let raw_vec: Vec<ffi::{ffi_ty}> = values.iter().map(|v| (*v).into()).collect();"#,
2886                    ffi_ty = ffi_ty
2887                ));
2888                body.push("let ptr = storage.push_vec(raw_vec);".to_string());
2889                body.push(format!(r#"raw.{ffi_field} = ptr;"#, ffi_field = ffi_field));
2890                if let Some(assign) = len_assign {
2891                    body.push(assign);
2892                }
2893            } else if index.struct_extensible(&member.member_type).is_some() {
2894                let ffi_ty = ffi_type_name(&member.member_type, c_prefix);
2895                body.push(format!(
2896                    r#"let mut raw_vec: Vec<ffi::{ffi_ty}> = Vec::with_capacity(values.len());"#,
2897                    ffi_ty = ffi_ty
2898                ));
2899                body.push("for item in values.iter() {".to_string());
2900                body.push("    let (raw_item, storage_item) = item.to_ffi();".to_string());
2901                body.push("    raw_vec.push(raw_item);".to_string());
2902                body.push("    storage.push_storage(storage_item);".to_string());
2903                body.push("}".to_string());
2904                body.push("let ptr = storage.push_vec(raw_vec);".to_string());
2905                body.push(format!(r#"raw.{ffi_field} = ptr;"#, ffi_field = ffi_field));
2906                if let Some(assign) = len_assign {
2907                    body.push(assign);
2908                }
2909            } else if member.member_type == "string view" {
2910                body.push(format!(
2911                    r#"let raw_vec: Vec<ffi::{prefix}StringView> = values.iter().map(|value| ffi::{prefix}StringView {{ data: value.as_ptr().cast(), length: value.len() }}).collect();"#,
2912                    prefix = c_prefix
2913                ));
2914                body.push("let ptr = storage.push_vec(raw_vec);".to_string());
2915                body.push(format!(r#"raw.{ffi_field} = ptr;"#, ffi_field = ffi_field));
2916                if let Some(assign) = len_assign {
2917                    body.push(assign);
2918                }
2919            } else {
2920                body.push("let raw_vec = values.to_vec();".to_string());
2921                body.push("let ptr = storage.push_vec(raw_vec);".to_string());
2922                body.push(format!(r#"raw.{ffi_field} = ptr;"#, ffi_field = ffi_field));
2923                if let Some(assign) = len_assign {
2924                    body.push(assign);
2925                }
2926            }
2927
2928            lines.push(format!(
2929                r#"if let Some(values) = &self.{field} {{
2930{body}
2931}} else {{
2932    raw.{ffi_field} = std::ptr::null();
2933    {len_zero}
2934}}"#,
2935                field = field_name,
2936                ffi_field = ffi_field,
2937                body = indent_block(&body.join("\n"), 4),
2938                len_zero = if let Some((_, field)) = len_field.as_ref() {
2939                    format!("raw.{field} = 0;", field = field)
2940                } else {
2941                    "let _ = 0;".to_string()
2942                }
2943            ));
2944            continue;
2945        }
2946
2947        if member.member_type == "string view" {
2948            lines.push(format!(
2949                r#"if let Some(value) = &self.{field} {{
2950    raw.{ffi_field} = ffi::{prefix}StringView {{ data: value.as_ptr().cast(), length: value.len() }};
2951}} else {{
2952    raw.{ffi_field} = ffi::{prefix}StringView {{ data: std::ptr::null(), length: 0 }};
2953}}"#,
2954                field = field_name,
2955                ffi_field = ffi_field,
2956                prefix = c_prefix
2957            ));
2958            continue;
2959        }
2960
2961        if member.member_type == "bool" {
2962            lines.push(format!(
2963                r#"raw.{ffi_field} = if self.{field}.unwrap_or(false) {{ 1 }} else {{ 0 }};"#,
2964                ffi_field = ffi_field,
2965                field = field_name
2966            ));
2967            continue;
2968        }
2969
2970        if index.is_enum(&member.member_type) || index.is_bitmask(&member.member_type) {
2971            let ffi_ty = ffi_type_name(&member.member_type, c_prefix);
2972            lines.push(format!(
2973                r#"if let Some(value) = self.{field} {{
2974    raw.{ffi_field} = value.into();
2975}} else {{
2976    raw.{ffi_field} = 0 as ffi::{ffi_ty};
2977}}"#,
2978                field = field_name,
2979                ffi_field = ffi_field,
2980                ffi_ty = ffi_ty
2981            ));
2982            continue;
2983        }
2984
2985        if index.is_object(&member.member_type) {
2986            lines.push(format!(
2987                r#"raw.{ffi_field} = self.{field}.as_ref().map(|v| v.as_raw()).unwrap_or(std::ptr::null_mut());"#,
2988                ffi_field = ffi_field,
2989                field = field_name
2990            ));
2991            continue;
2992        }
2993
2994        if index.is_callback_info(&member.member_type) {
2995            if let Some(callback_fn_name) = callback_info_map.get(&member.member_type) {
2996                let callback_ty = callback_type_name(callback_fn_name);
2997                let ffi_callback_ty = ffi_type_name(callback_fn_name, c_prefix);
2998                let trampoline = callback_trampoline_name(callback_fn_name);
2999                let info_name = type_name(&member.member_type);
3000                let has_mode = callback_info_mode_map
3001                    .get(&member.member_type)
3002                    .copied()
3003                    .unwrap_or(false);
3004                let mode_line = if has_mode {
3005                    "    let mode = info.mode.unwrap_or(CallbackMode::AllowSpontaneous);\n"
3006                } else {
3007                    ""
3008                };
3009                let mode_field = if has_mode {
3010                    "        mode: mode.into(),\n"
3011                } else {
3012                    ""
3013                };
3014                let default_mode_field = if has_mode {
3015                    "        mode: CallbackMode::AllowSpontaneous.into(),\n"
3016                } else {
3017                    ""
3018                };
3019
3020                lines.push(format!(
3021                    r#"if let Some(info) = &self.{field} {{
3022    let mut callback_slot = info.callback.borrow_mut();
3023    let callback = callback_slot.take();
3024    let (callback_ptr, userdata1): (ffi::{ffi_callback_ty}, *mut std::ffi::c_void) = if let Some(callback) = callback {{
3025        let callback_box: {callback_ty} = callback;
3026        let callback_box = Box::new(Some(callback_box));
3027        let userdata = Box::into_raw(callback_box).cast::<std::ffi::c_void>();
3028        (Some({trampoline}), userdata)
3029    }} else {{
3030        (None, std::ptr::null_mut())
3031    }};
3032{mode_line}    raw.{ffi_field} = ffi::{prefix}{info_name} {{
3033        nextInChain: std::ptr::null_mut(),
3034{mode_field}        callback: callback_ptr,
3035        userdata1,
3036        userdata2: std::ptr::null_mut(),
3037    }};
3038}} else {{
3039    raw.{ffi_field} = ffi::{prefix}{info_name} {{
3040        nextInChain: std::ptr::null_mut(),
3041{default_mode_field}        callback: None,
3042        userdata1: std::ptr::null_mut(),
3043        userdata2: std::ptr::null_mut(),
3044    }};
3045}}"#,
3046                    field = field_name,
3047                    callback_ty = callback_ty,
3048                    ffi_callback_ty = ffi_callback_ty,
3049                    trampoline = trampoline,
3050                    mode_line = mode_line,
3051                    mode_field = mode_field,
3052                    default_mode_field = default_mode_field,
3053                    ffi_field = ffi_field,
3054                    info_name = info_name,
3055                    prefix = c_prefix
3056                ));
3057            } else {
3058                lines.push(format!(r#"let _ = &self.{field};"#, field = field_name));
3059            }
3060            continue;
3061        }
3062
3063        if index.struct_extensible(&member.member_type).is_some() {
3064            if member.annotation.is_value() {
3065                lines.push(format!(
3066                    r#"if let Some(value) = &self.{field} {{
3067    let (raw_value, storage_value) = value.to_ffi();
3068    raw.{ffi_field} = raw_value;
3069    storage.push_storage(storage_value);
3070}}"#,
3071                    field = field_name,
3072                    ffi_field = ffi_field
3073                ));
3074                continue;
3075            }
3076
3077            if member.annotation.is_const_ptr() {
3078                lines.push(format!(
3079                    r#"if let Some(value) = &self.{field} {{
3080    let (raw_value, storage_value) = value.to_ffi();
3081    let ptr = storage.push_value(raw_value);
3082    raw.{ffi_field} = ptr;
3083    storage.push_storage(storage_value);
3084}} else {{
3085    raw.{ffi_field} = std::ptr::null();
3086}}"#,
3087                    field = field_name,
3088                    ffi_field = ffi_field
3089                ));
3090                continue;
3091            }
3092
3093            if member.annotation.is_mut_ptr() {
3094                lines.push(format!(
3095                    r#"if let Some(value) = &self.{field} {{
3096    let (raw_value, storage_value) = value.to_ffi();
3097    let ptr = storage.push_value_mut(raw_value);
3098    raw.{ffi_field} = ptr;
3099    storage.push_storage(storage_value);
3100}} else {{
3101    raw.{ffi_field} = std::ptr::null_mut();
3102}}"#,
3103                    field = field_name,
3104                    ffi_field = ffi_field
3105                ));
3106                continue;
3107            }
3108        }
3109
3110        lines.push(format!(
3111            r#"if let Some(value) = self.{field} {{
3112    raw.{ffi_field} = value;
3113}}"#,
3114            field = field_name,
3115            ffi_field = ffi_field
3116        ));
3117    }
3118
3119    lines.push("(raw, storage)".to_string());
3120
3121    lines.join("\n")
3122}