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
1508struct 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}