1use std::{collections::HashMap, fmt::Write as _};
2
3use anyhow::{Context, Result, anyhow, bail};
4
5use handlebars::Handlebars;
6use heck::ToSnakeCase as _;
7use serde_json::json;
8use rs_schema::{
9 FunctionArgSchema, FunctionSchema, GenericParamSchema, TraitSchema, TypeSchema,
10};
11
12use crate::{
13 cffi_type_utils::{cffi_ty_stack_to_rust_cffi_ty, cffi_type_str_to_type_stack},
14 codegen::CodeGen,
15 rs_type_utils::{
16 annotation_transforms_for_arg, fn_arg_to_async_cffi_type_spec, substitute_generic_args,
17 },
18};
19
20#[derive(Debug)]
22pub struct RsFileSchema {
23 pub structs: Vec<RsStructSchema>,
24 pub trait_impls: Vec<RsTraitImplSchema>,
25}
26
27impl CodeGen for RsFileSchema {
28 fn codegen(&self, indent: usize) -> String {
30 let mut output = String::new();
31 writeln!(
32 &mut output,
33 "// This file is autogenerated. Do not edit directly."
34 )
35 .unwrap();
36 writeln!(
37 &mut output,
38 "use std::{{ffi::{{c_ulong, c_void}}, sync::Arc}};\n"
39 )
40 .unwrap();
41 writeln!(
42 &mut output,
43 "use async_cffi::{{CffiFuture, CffiPointerBuffer, SafePtr}};\n"
44 )
45 .unwrap();
46 writeln!(&mut output, "use futures::future::BoxFuture;\n").unwrap();
47 writeln!(
48 &mut output,
49 "use n_observer::{{AnyArc, InnerObserverReceiver, Observable, Publisher}};\n"
50 )
51 .unwrap();
52 if self.structs.iter().any(|s| s.name == "CffiObservable") {
53 writeln!(&mut output, "use crate::CffiPublisher;\n").unwrap();
54 }
55 if self
56 .structs
57 .iter()
58 .any(|s| s.name == "CffiPublisher" || s.name == "CffiObservable")
59 {
60 writeln!(&mut output, "use crate::CffiInnerObserverReceiver;\n").unwrap();
61 }
62 for s in &self.structs {
63 writeln!(&mut output, "{}", s.codegen(indent)).unwrap();
64 }
65 writeln!(&mut output).unwrap();
66 for ti in &self.trait_impls {
67 writeln!(&mut output, "{}\n", ti.codegen(indent)).unwrap();
68 }
69 output
70 }
71}
72
73#[derive(Debug)]
75pub struct RsStructSchema {
76 macros: Vec<String>,
77 name: String,
78 fields: Vec<RsFieldSchema>,
79}
80
81impl CodeGen for RsStructSchema {
82 fn codegen(&self, indent: usize) -> String {
84 let pad = " ".repeat(indent);
85 let mut output = String::new();
86 for m in &self.macros {
87 writeln!(&mut output, "{}{}", pad, m).unwrap();
88 }
89
90 writeln!(&mut output, "{}pub struct {} {{", pad, self.name).unwrap();
91 for field in &self.fields {
92 writeln!(&mut output, "{}", field.codegen(indent + 1)).unwrap();
93 }
94 write!(&mut output, "{}}}", pad).unwrap();
95 output
96 }
97}
98
99#[derive(Debug)]
101pub struct RsFieldSchema {
102 name: String,
103 field_type: TypeSchema,
104}
105
106impl CodeGen for RsFieldSchema {
107 fn codegen(&self, indent: usize) -> String {
109 let pad = " ".repeat(indent);
110 format!("{}pub {}: {},", pad, self.name, self.field_type.codegen(0))
111 }
112}
113
114#[derive(Debug)]
116pub struct RsTraitImplSchema {
117 pub trait_name: String,
118 pub impl_for: Option<String>,
119 pub generic_args: Vec<String>,
120 pub functions: Vec<FunctionSchema>,
121 pub unsafe_impl: bool,
122 pub comment_out: bool,
123}
124
125impl CodeGen for RsTraitImplSchema {
126 fn codegen(&self, indent: usize) -> String {
128 let pad = " ".repeat(indent);
129 let mut output = String::new();
130 if self.comment_out {
131 writeln!(&mut output, "{}/*", pad).unwrap();
132 }
133 let unsafe_str = if self.unsafe_impl { "unsafe " } else { "" };
134 let impl_for_str = if let Some(impl_for) = &self.impl_for {
135 format!("for {} ", impl_for)
136 } else {
137 String::new()
138 };
139 let generic_args_str = if self.generic_args.len() == 0 {
140 String::new()
141 } else {
142 let args = self.generic_args.join(", ");
143 format!("<{}>", args)
144 };
145 writeln!(
146 &mut output,
147 "{}{}impl {}{} {}{{",
148 pad, unsafe_str, self.trait_name, generic_args_str, impl_for_str
149 )
150 .unwrap();
151 for func in &self.functions {
152 let funct = func.codegen(indent + 1);
153 funct
154 .split_inclusive('\n')
155 .for_each(|line| writeln!(&mut output, "{}", line).unwrap());
156 }
157 write!(&mut output, "{}}}", pad).unwrap();
158 if self.comment_out {
159 writeln!(&mut output, "\n{}*/", pad).unwrap();
160 }
161 output
162 }
163}
164
165impl CodeGen for FunctionSchema {
166 fn codegen(&self, indent: usize) -> String {
168 let pad = " ".repeat(indent);
169 let mut output = String::new();
170 format!("{}", self)
171 .split_inclusive('\n')
172 .for_each(|line| write!(&mut output, "{}{}", pad, line).unwrap());
173 output
174 }
175}
176
177struct FnArgTransforms {
179 pub transforms: Option<String>,
180 pub returns_safe_ptr: bool,
181}
182
183pub fn trait_to_async_cffi_schema(
185 trait_schema: &TraitSchema,
186 supertrait_schemas: &HashMap<String, TraitSchema>,
187) -> Result<RsFileSchema> {
188 let cffi_struct_schema = create_cffi_struct_schema(trait_schema)?;
189
190 let supertrait_impl_schemas = trait_schema
191 .supertraits
192 .iter()
193 .filter(|st| st.ty != "Send" && st.ty != "Sync")
194 .map(|st| {
195 supertrait_schemas
196 .get(&st.ty)
197 .with_context(|| format!("Missing supertrait schema: {}", st.ty))
198 })
199 .collect::<Result<Vec<_>, _>>()?
200 .into_iter()
201 .map(|st| create_supertrait_impl(st, &cffi_struct_schema))
202 .collect::<Result<Vec<_>, _>>()?;
203
204 let trait_impl_schema =
205 trait_to_async_cffi_trait_impl_schema(trait_schema, &cffi_struct_schema)?;
206
207 let send_impl_schema = RsTraitImplSchema {
208 trait_name: "Send".to_string(),
209 impl_for: Some(cffi_struct_schema.name.clone()),
210 generic_args: vec![],
211 functions: vec![],
212 unsafe_impl: true,
213 comment_out: false,
214 };
215
216 let sync_impl_schema = RsTraitImplSchema {
217 trait_name: "Sync".to_string(),
218 impl_for: Some(cffi_struct_schema.name.clone()),
219 generic_args: vec![],
220 functions: vec![],
221 unsafe_impl: true,
222 comment_out: false,
223 };
224
225 let cffi_struct_impls = cffi_struct_impls(trait_schema, &cffi_struct_schema)?;
226
227 let dyn_rust_trait_to_cffi = RsTraitImplSchema {
228 trait_name: "From".to_string(),
229 impl_for: Some(cffi_struct_schema.name.clone()),
230 functions: vec![trait_schema_to_from_impl(trait_schema)?],
231 generic_args: vec![format!(
232 "&dyn {}",
233 get_trait_name_w_subbed_generics(trait_schema)?
234 )],
235 unsafe_impl: false,
236 comment_out: false,
237 };
238
239 Ok(RsFileSchema {
240 structs: vec![cffi_struct_schema],
241 trait_impls: {
242 let mut trait_impls = supertrait_impl_schemas;
243 trait_impls.extend(vec![
244 trait_impl_schema,
245 send_impl_schema,
246 sync_impl_schema,
247 cffi_struct_impls,
248 dyn_rust_trait_to_cffi,
249 ]);
250 trait_impls
251 },
252 })
253}
254
255fn cffi_struct_impls(
256 trait_schema: &TraitSchema,
257 cffi_struct_schema: &RsStructSchema,
258) -> Result<RsTraitImplSchema> {
259 let as_supertrait_fns = trait_schema
260 .supertraits
261 .iter()
262 .filter(|st| st.ty != "Send" && st.ty != "Sync")
263 .map(|st| create_as_supertrait_fn(st))
264 .collect::<Result<Vec<_>, _>>()?;
265 let trait_cffi_impls = trait_schema
266 .functions
267 .iter()
268 .map(|func| trait_fn_to_cffi_c_fn(func, trait_schema, &trait_schema.generics))
269 .collect::<Result<Vec<_>, _>>()?;
270 Ok(RsTraitImplSchema {
271 trait_name: cffi_struct_schema.name.clone(),
272 impl_for: None,
273 generic_args: vec![],
274 functions: {
275 let mut functions = as_supertrait_fns;
276 functions.extend(trait_cffi_impls);
277 functions
278 },
279 unsafe_impl: false,
280 comment_out: false,
281 })
282}
283
284fn create_as_supertrait_fn(supertrait: &TypeSchema) -> Result<FunctionSchema> {
285 Ok(FunctionSchema {
286 name: format!("as_{}", supertrait.ty.to_snake_case()),
287 args: vec![FunctionArgSchema {
288 name: "self".to_string(),
289 ty: None,
290 annotations: None,
291 }],
292 return_type: TypeSchema {
293 ty: format!("&dyn {}", supertrait.ty),
294 generic_ty_args: supertrait.generic_ty_args.clone(),
295 },
296 body: Some(format!("&self.cffi_{}", supertrait.ty.to_snake_case())),
297 extern_layout: None,
298 annotations: None,
299 })
300}
301
302fn create_supertrait_impl(
303 supertrait: &TraitSchema,
304 cffi_struct_schema: &RsStructSchema,
305) -> Result<RsTraitImplSchema> {
306 let functions = supertrait
307 .functions
308 .iter()
309 .map(|func| supertrait_fn_to_async_cffi_fn(supertrait, func, &supertrait.generics))
310 .collect::<Result<Vec<_>, _>>()?;
311 Ok(RsTraitImplSchema {
312 trait_name: supertrait.name.clone(),
313 impl_for: Some(cffi_struct_schema.name.clone()),
314 generic_args: supertrait
315 .generics
316 .iter()
317 .map(|p| {
318 p.annotations
319 .as_ref()
320 .and_then(|a| a.cffi_type.as_ref())
321 .ok_or_else(|| {
322 anyhow!("Missing cffi_type annotation for trait generic argument")
323 })
324 .and_then(|cffi_ty| {
325 cffi_type_str_to_type_stack(cffi_ty)
326 .and_then(|stack| cffi_ty_stack_to_rust_cffi_ty(&stack))
327 })
328 })
329 .collect::<Result<Vec<_>, _>>()?,
330 functions,
331 unsafe_impl: false,
332 comment_out: false,
333 })
334}
335
336fn trait_to_async_cffi_trait_impl_schema(
337 trait_schema: &TraitSchema,
338 cffi_struct_schema: &RsStructSchema,
339) -> Result<RsTraitImplSchema> {
340 let functions = trait_schema
341 .functions
342 .iter()
343 .map(|func| trait_fn_to_async_cffi_fn(func, &trait_schema.generics))
344 .collect::<Result<Vec<_>, _>>()?;
345 Ok(RsTraitImplSchema {
346 trait_name: trait_schema.name.clone(),
347 impl_for: Some(cffi_struct_schema.name.clone()),
348 generic_args: cffi_substituted_trait_generic_arg_types(trait_schema)?,
349 functions,
350 unsafe_impl: false,
351 comment_out: false,
352 })
353}
354
355fn cffi_substituted_trait_generic_arg_types(trait_schema: &TraitSchema) -> Result<Vec<String>> {
356 Ok(trait_schema
357 .generics
358 .iter()
359 .map(|p| {
360 p.annotations
361 .as_ref()
362 .and_then(|a| a.cffi_type.as_ref())
363 .ok_or_else(|| anyhow!("Missing cffi_type annotation for trait generic argument"))
364 .and_then(|cffi_ty| {
365 cffi_type_str_to_type_stack(cffi_ty)
366 .and_then(|stack| cffi_ty_stack_to_rust_cffi_ty(&stack))
367 })
368 })
369 .collect::<Result<Vec<_>, _>>()?)
370}
371
372fn create_cffi_struct_schema(trait_schema: &TraitSchema) -> Result<RsStructSchema> {
374 let macros = vec![
375 "#[repr(C)]".to_string(),
376 "#[derive(Debug, Clone)]".to_string(),
377 ];
378 let name = format!("Cffi{}", trait_schema.name);
379
380 let supertrait_fields = trait_schema
381 .supertraits
382 .iter()
383 .filter(|st| st.ty != "Send" && st.ty != "Sync")
384 .map(|st| RsFieldSchema {
385 name: format!("cffi_{}", st.ty.to_snake_case()),
386 field_type: TypeSchema::new_simple(format!("Cffi{}", st.ty)),
387 })
388 .collect::<Vec<_>>();
389
390 let trait_fn_fields = trait_schema
391 .functions
392 .iter()
393 .map(|func| {
394 func.args
395 .iter()
396 .map(arg_to_field_type)
397 .collect::<Result<Vec<TypeSchema>>>()
398 .map(|args| RsFieldSchema {
399 name: format!("{}_fut", func.name),
400 field_type: TypeSchema::new_simple(format!(
401 "extern \"C\" fn({}) -> *const c_void",
402 args.into_iter()
403 .map(|ty| ty.codegen(0))
404 .collect::<Vec<_>>()
405 .join(", ")
406 )),
407 })
408 })
409 .collect::<Result<Vec<RsFieldSchema>, _>>()?;
410
411 let fields = {
412 let mut fs = vec![RsFieldSchema {
413 name: "self_ptr".to_string(),
414 field_type: TypeSchema::new_simple("*const c_void".to_string()),
415 }];
416 fs.extend(supertrait_fields);
417 fs.extend(trait_fn_fields);
418 fs
419 };
420
421 let struct_schema = RsStructSchema {
422 macros,
423 name,
424 fields,
425 };
426
427 Ok(struct_schema)
428}
429
430fn supertrait_fn_to_async_cffi_fn(
431 supertrait: &TraitSchema,
432 func: &FunctionSchema,
433 generics: &Vec<GenericParamSchema>,
434) -> Result<FunctionSchema> {
435 let ret_substituted = substitute_generic_args(&func.return_type, generics)?;
436 Ok(FunctionSchema {
437 name: func.name.clone(),
438 args: func
439 .args
440 .iter()
441 .map(|arg| FunctionArgSchema {
442 name: arg.name.clone(),
443 ty: arg.ty.clone(),
444 annotations: None,
445 })
446 .collect(),
447 return_type: ret_substituted,
448 body: Some(supertrait_fn_to_async_cffi_fn_body(supertrait, func)?),
449 extern_layout: None,
450 annotations: None,
451 })
452}
453
454fn arg_to_field_type(arg: &FunctionArgSchema) -> Result<TypeSchema> {
456 let cffi_type_stack = fn_arg_to_async_cffi_type_spec(arg)?;
457 let cffi_type = cffi_type_stack
458 .into_iter()
459 .next()
460 .and_then(|ty| ty.explicit_type);
461
462 Ok(TypeSchema {
463 ty: cffi_type.unwrap_or("*const c_void".to_string()),
464 generic_ty_args: vec![],
465 })
466}
467
468fn trait_fn_to_async_cffi_fn(
470 func: &FunctionSchema,
471 generics: &Vec<GenericParamSchema>,
472) -> Result<FunctionSchema> {
473 let ret_substituted = substitute_generic_args(&func.return_type, generics)?;
474 Ok(FunctionSchema {
475 name: func.name.clone(),
476 args: func
477 .args
478 .iter()
479 .map(|arg| FunctionArgSchema {
480 name: arg.name.clone(),
481 ty: arg.ty.clone(),
482 annotations: None,
483 })
484 .collect(),
485 return_type: ret_substituted,
486 body: Some(trait_fn_to_async_cffi_fn_body(func, generics)?),
487 extern_layout: None,
488 annotations: None,
489 })
490}
491
492fn trait_fn_to_cffi_c_fn(
494 func: &FunctionSchema,
495 trait_schema: &TraitSchema,
496 generics: &Vec<GenericParamSchema>,
497) -> Result<FunctionSchema> {
498 Ok(FunctionSchema {
499 name: format!("{}_fut_impl", func.name),
500 args: func
501 .args
502 .iter()
503 .map(|a| arg_to_field_type(a).map(|s| (a, s)))
504 .collect::<Result<Vec<_>, _>>()?
505 .into_iter()
506 .map(|(a, at)| FunctionArgSchema {
507 name: if a.name == "self" {
508 "self_ptr".to_string()
509 } else {
510 a.name.clone()
511 },
512 ty: Some(at.clone()),
513 annotations: None,
514 })
515 .collect(),
516 return_type: TypeSchema {
517 ty: "*const c_void".to_string(),
518 generic_ty_args: vec![],
519 },
520 body: Some(trait_fn_to_cffi_c_fn_body(func, trait_schema, generics)?),
521 extern_layout: Some("C".to_string()),
522 annotations: None,
523 })
524}
525
526fn trait_fn_to_cffi_c_fn_body(
528 func: &FunctionSchema,
529 trait_schema: &TraitSchema,
530 generics: &Vec<GenericParamSchema>,
531) -> Result<String> {
532 let mut fn_body = String::new();
533
534 if let Some(annotations) = &func.annotations {
535 if annotations.cffi_impl_no_op {
536 writeln!(fn_body, "// no-op").unwrap();
537 writeln!(
538 fn_body,
539 "CffiFuture::from_rust_future_boxed(async move {{}}).into_raw()"
540 )
541 .unwrap();
542 return Ok(fn_body);
543 }
544 }
545
546 let arg_transforms = func
547 .args
548 .iter()
549 .map(|arg| cffi_c_fn_arg_to_transforms(arg, trait_schema))
550 .collect::<Result<Vec<String>>>()?;
551
552 arg_transforms
553 .iter()
554 .try_for_each(|t| writeln!(fn_body, "{}\n", t))
555 .context("Failed to write argument transforms for CFFI trampoline")?;
556
557 let ret_ty = &func.return_type;
559 if ret_ty.ty != "BoxFuture" && ret_ty.generic_ty_args.len() != 2 {
560 bail!("Only BoxFuture returning trait functions are supported");
561 }
562 let async_ret_ty = &ret_ty.generic_ty_args[1];
563 if async_ret_ty.ty == "()" {
564 writeln!(
566 fn_body,
567 "let fut = self_ref.{}({});",
568 func.name,
569 func.args
570 .iter()
571 .skip(1)
572 .map(|a| a.name.clone())
573 .collect::<Vec<String>>()
574 .join(", ")
575 )
576 .unwrap();
577
578 writeln!(
579 fn_body,
580 "CffiFuture::from_rust_future_boxed(fut).into_raw()"
581 )
582 .unwrap();
583 } else {
584 writeln!(
586 fn_body,
587 "let fut = Box::pin(async move {{\n let ret = self_ref.{}({}).await;",
588 func.name,
589 func.args
590 .iter()
591 .skip(1)
592 .map(|a| a.name.clone())
593 .collect::<Vec<String>>()
594 .join(", ")
595 )
596 .unwrap();
597
598 let async_ret_ty_subbed = substitute_generic_args(async_ret_ty, generics)?;
599 writeln!(
600 fn_body,
601 "{}",
602 type_transforms_for_cffi_c_fn_return(&async_ret_ty_subbed, "ret")?
603 )
604 .unwrap();
605
606 writeln!(fn_body, " SafePtr(ret)").unwrap();
607
608 writeln!(fn_body, "}});").unwrap();
609
610 writeln!(
611 fn_body,
612 "CffiFuture::from_rust_future_boxed(fut).into_raw()"
613 )
614 .unwrap();
615 }
616
617 Ok(fn_body)
618}
619
620fn cffi_c_fn_arg_to_transforms(
622 arg: &FunctionArgSchema,
623 trait_schema: &TraitSchema,
624) -> Result<String> {
625 let target_type = fn_arg_to_async_cffi_type_spec(arg)?;
626 let target_type = target_type.into_iter().next().unwrap();
627
628 if let Some(arg_ty) = &arg.ty {
629 if let Some(explicit_type) = target_type.explicit_type.as_ref() {
630 match explicit_type.as_str() {
631 "CffiPointerBuffer" => {
632 let mut transform = format!("let {} = {}\n", arg.name, arg.name);
633
634 writeln!(transform, " .as_slice()").unwrap();
635 writeln!(transform, " .iter()").unwrap();
636 writeln!(transform, " .copied()").unwrap();
637 writeln!(transform, " .map(|value| {{").unwrap();
638 writeln!(transform, " if !value.is_null() {{").unwrap();
639 writeln!(transform, " let value = SafePtr(value);").unwrap();
640 writeln!(transform, " Some(Arc::new(value) as AnyArc)").unwrap();
641 writeln!(transform, " }} else {{").unwrap();
642 writeln!(transform, " None").unwrap();
643 writeln!(transform, " }}").unwrap();
644 writeln!(transform, " }})").unwrap();
645 writeln!(transform, " .collect();").unwrap();
646
647 Ok(transform)
648 }
649 "c_ulong" => {
650 let transform = format!("let {} = {} as usize;", arg.name, arg.name);
651 Ok(transform)
652 }
653 explicit_type => {
654 if explicit_type.starts_with("Cffi") {
655 let transform = format!("let {} = Box::new({});", arg.name, arg.name);
656 Ok(transform)
657 } else {
658 Ok(String::new())
659 }
660 }
661 }
662 } else {
663 if arg_ty.ty == "AnyArc" {
664 let transform = format!("let {} = Arc::new(SafePtr({}));", arg.name, arg.name);
665 Ok(transform)
666 } else {
667 Ok(String::new())
668 }
669 }
670 } else {
671 let mut transform = String::new();
672
673 writeln!(transform, "let self_ref = unsafe {{").unwrap();
674 writeln!(
675 transform,
676 " (self_ptr as *const &(dyn {} + Send + Sync))",
677 get_trait_name_w_subbed_generics(trait_schema)?,
678 )
679 .unwrap();
680 writeln!(transform, " .as_ref()").unwrap();
681 writeln!(transform, " .expect(\"Self pointer cannot be null\")").unwrap();
682 writeln!(transform, "}};").unwrap();
683
684 Ok(transform)
685 }
686}
687
688fn get_trait_name_w_subbed_generics(trait_schema: &TraitSchema) -> Result<String> {
689 let generic_args = if trait_schema.generics.len() > 0 {
690 format!(
691 "<{}>",
692 cffi_substituted_trait_generic_arg_types(trait_schema)?.join(", ")
693 )
694 } else {
695 String::new()
696 };
697
698 Ok(format!("{}{}", trait_schema.name, generic_args))
699}
700
701fn trait_fn_to_async_cffi_fn_body(
703 func: &FunctionSchema,
704 generics: &Vec<GenericParamSchema>,
705) -> Result<String> {
706 let mut fn_body = String::new();
707
708 let arg_assertions = func
709 .args
710 .iter()
711 .map(fn_arg_to_assertions)
712 .collect::<Vec<String>>()
713 .join("");
714 fn_body.push_str(&arg_assertions);
715 fn_body.push('\n');
716
717 let arg_transforms = func
718 .args
719 .iter()
720 .map(fn_arg_to_transforms)
721 .collect::<Result<Vec<FnArgTransforms>>>()?;
722 let safe_ptr_args = arg_transforms
723 .iter()
724 .map(|ts| ts.returns_safe_ptr)
725 .collect::<Vec<_>>();
726 let arg_transforms = arg_transforms
727 .into_iter()
728 .map(|ts| ts.transforms.unwrap_or_default())
729 .collect::<Vec<_>>()
730 .join("");
731 fn_body.push_str(&arg_transforms);
732 fn_body.push('\n');
733
734 let async_fn_isolation = format!("let {}_fn = self.{}_fut;", func.name, func.name);
735 fn_body.push_str(&async_fn_isolation);
736 fn_body.push('\n');
737
738 let wrapped_self_ptr = "let self_ptr = SafePtr(self.self_ptr);";
739 fn_body.push_str(wrapped_self_ptr);
740 fn_body.push('\n');
741
742 let fn_async_block = async_block_for_trait_fn(func, safe_ptr_args, generics)?;
743 fn_body.push_str(&fn_async_block);
744 fn_body.push('\n');
745
746 Ok(fn_body)
747}
748
749fn supertrait_fn_to_async_cffi_fn_body(
750 supertrait: &TraitSchema,
751 func: &FunctionSchema,
752) -> Result<String> {
753 let args = func
754 .args
755 .iter()
756 .skip(1) .map(|a| a.name.clone())
758 .collect::<Vec<_>>()
759 .join(", ");
760 Ok(format!(
761 "self.as_{}().{}({})",
762 supertrait.name.to_snake_case(),
763 func.name,
764 args,
765 ))
766}
767
768fn async_block_for_trait_fn(
770 func: &FunctionSchema,
771 safe_ptr_args: Vec<bool>,
772 generics: &Vec<GenericParamSchema>,
773) -> Result<String> {
774 let mut async_block = "Box::pin(async move {\n".to_string();
775 writeln!(&mut async_block, "let self_ptr = self_ptr;").unwrap();
776
777 func.args
778 .iter()
779 .skip(1)
780 .for_each(|arg| writeln!(&mut async_block, "let {} = {};", arg.name, arg.name).unwrap());
781
782 let cffi_call_args = func
783 .args
784 .iter()
785 .zip(safe_ptr_args.iter())
786 .skip(1)
787 .map(|(arg, is_safe_ptr)| {
788 let safe_ptr_access = if *is_safe_ptr { ".0" } else { "" };
789 format!(", {}{}", arg.name, safe_ptr_access)
790 })
791 .collect::<Vec<_>>()
792 .join("");
793
794 writeln!(
795 &mut async_block,
796 "let fut = ({}_fn)(self_ptr.0{});",
797 func.name, cffi_call_args
798 )
799 .unwrap();
800
801 writeln!(
802 &mut async_block,
803 "if fut.is_null() {{\n panic!(\"C function returned null pointer\");\n}}"
804 )
805 .unwrap();
806
807 writeln!(&mut async_block, "let fut = unsafe {{\n // Convert the raw pointer to a CffiFuture\n (fut as *mut CffiFuture)\n .as_mut()\n .expect(\"CffiFuture cannot be null\")\n}};").unwrap();
808
809 let ret_ty = &func.return_type;
810 let outer_ty = &ret_ty.ty;
811 if outer_ty != "BoxFuture" {
812 bail!("Only BoxFuture returning trait functions currently supported");
813 }
814
815 if ret_ty.generic_ty_args.len() == 2 && &ret_ty.generic_ty_args[1].ty != "()" {
816 writeln!(&mut async_block, "let ret = fut.await;").unwrap();
818 writeln!(&mut async_block, "assert!(!ret.is_null());").unwrap();
819
820 let async_return_ty = &ret_ty.generic_ty_args[1];
821 let async_return_ty = substitute_generic_args(async_return_ty, generics)?;
822
823 async_block.push_str(&type_transforms_for_cffi_fut_ptr(&async_return_ty, "ret")?);
824
825 writeln!(&mut async_block, "ret").unwrap();
827 } else {
828 writeln!(&mut async_block, "fut.await;").unwrap();
830 }
831
832 writeln!(&mut async_block, "}})").unwrap();
833
834 Ok(async_block)
835}
836
837fn fn_arg_to_assertions(arg: &FunctionArgSchema) -> String {
839 if let Some(annotations) = &arg.annotations {
840 if let Some(assert_len) = annotations.assert_len {
841 return format!(
842 "if {}.len() != {} {{\n panic!(\"Expected {} to have length {}\"); \n}} \n",
843 arg.name, assert_len, arg.name, assert_len
844 );
845 }
846 }
847 "".to_string()
848}
849
850fn fn_arg_to_transforms(arg: &FunctionArgSchema) -> Result<FnArgTransforms> {
852 let mut transforms = String::new();
853 let target_type = fn_arg_to_async_cffi_type_spec(arg)?;
854
855 let annotation_transforms = annotation_transforms_for_arg(arg, target_type)?;
856 annotation_transforms.map(|t| transforms.push_str(&t));
857
858 let type_transforms = type_transforms_for_arg(arg)?;
859 type_transforms.transforms.map(|t| transforms.push_str(&t));
860
861 Ok(FnArgTransforms {
862 transforms: Some(transforms),
863 returns_safe_ptr: type_transforms.returns_safe_ptr,
864 })
865}
866
867fn type_transforms_for_arg(arg: &FunctionArgSchema) -> Result<FnArgTransforms> {
869 let mut returns_safe_ptr = true;
870 let type_transform = match arg.ty.as_ref() {
871 None => None,
872 Some(fn_arg_type_schema) => {
873 let reg = Handlebars::new();
874 let transform_block = render_fn_arg_type_schema(
875 ®,
876 &fn_arg_type_schema,
877 format!("let {} = {{{{{{next}}}}}};\n", arg.name),
878 false,
879 &mut returns_safe_ptr,
880 &arg.name,
881 )?;
882
883 Some(transform_block)
884 }
885 };
886
887 Ok(FnArgTransforms {
888 transforms: type_transform,
889 returns_safe_ptr,
890 })
891}
892
893fn render_fn_arg_type_schema(
894 reg: &Handlebars,
895 type_schema: &TypeSchema,
896 mut transform_block: String,
897 current_is_box: bool,
898 returns_safe_ptr: &mut bool,
899 arg_name: &str,
900) -> Result<String> {
901 let mut next_is_box = current_is_box;
902
903 let next_schema = match type_schema.ty.as_str() {
904 "Option" => {
905 let template_string = format!(
906 "if let Some({}) = {} {{\n {{{{{{next}}}}}}\n}} else {{\n SafePtr(std::ptr::null())\n}}",
907 arg_name, arg_name,
908 );
909
910 transform_block =
911 reg.render_template(&transform_block, &json!({"next": template_string}))?;
912
913 type_schema.generic_ty_args.get(0)
914 }
915 "Vec" => {
916 let template_string = format!(
918 "CffiPointerBuffer::from_slice({}\n .into_iter()\n .map(|{}| {{\n ({{{{{{next}}}}}}).0\n}})\n .collect::<Box<[_]>>())",
919 arg_name, arg_name
920 );
921
922 *returns_safe_ptr = false;
923 transform_block =
924 reg.render_template(&transform_block, &json!({"next": template_string}))?;
925
926 type_schema.generic_ty_args.get(0)
927 }
928 "AnyArc" => {
929 let value_string = format!(
930 "*{}.downcast::<SafePtr>().expect(\"{} must be SafePtr\")",
931 arg_name, arg_name
932 );
933
934 transform_block =
935 reg.render_template(&transform_block, &json!({"next": value_string}))?;
936
937 type_schema.generic_ty_args.get(0)
938 }
939 "Arc" => {
940 let value_string = format!("SafePtr(Arc::into_raw({}) as *const c_void)", arg_name);
941
942 transform_block =
943 reg.render_template(&transform_block, &json!({"next": value_string}))?;
944
945 type_schema.generic_ty_args.get(0)
946 }
947 "Box" => {
948 next_is_box = true;
949 type_schema.generic_ty_args.get(0)
950 }
951 "usize" => {
952 let transform = format!("{} as c_ulong", arg_name);
953 transform_block = reg.render_template(&transform_block, &json!({"next": transform}))?;
954 *returns_safe_ptr = false;
955
956 type_schema.generic_ty_args.get(0)
957 }
958 fn_arg_type_elem => {
959 if current_is_box && fn_arg_type_elem.starts_with("dyn ") {
960 let mut transform = "{".to_string();
961 writeln!(
962 transform,
963 " let cffi_{} = (*{}).into();",
964 arg_name, arg_name
965 )
966 .unwrap();
967 writeln!(
968 transform,
969 " Box::leak({}); // Leak to keep alive",
970 arg_name
971 )
972 .unwrap();
973 writeln!(transform, " cffi_{}", arg_name).unwrap();
974 writeln!(transform, "}}").unwrap();
975
976 transform_block =
977 reg.render_template(&transform_block, &json!({"next": transform}))?;
978 *returns_safe_ptr = false;
979 }
980
981 type_schema.generic_ty_args.get(0)
982 }
983 };
984
985 if let Some(inner_schema) = next_schema {
986 render_fn_arg_type_schema(
987 reg,
988 inner_schema,
989 transform_block,
990 next_is_box,
991 returns_safe_ptr,
992 arg_name,
993 )
994 } else {
995 Ok(reg.render_template(&transform_block, &json!({"next": arg_name}))?)
996 }
997}
998
999fn type_transforms_for_cffi_fut_ptr(return_ty: &TypeSchema, retvar: &str) -> Result<String> {
1001 let reg = Handlebars::new();
1002 render_cffi_fut_ptr_transforms(
1003 ®,
1004 return_ty,
1005 format!("let {} = {{{{{{next}}}}}};\n", retvar),
1006 retvar,
1007 )
1008}
1009
1010fn type_transforms_for_cffi_c_fn_return(return_ty: &TypeSchema, retvar: &str) -> Result<String> {
1012 let reg = Handlebars::new();
1013 render_cffi_c_fn_return_transforms(
1014 ®,
1015 return_ty,
1016 format!("let {} = {{{{{{next}}}}}};\n", retvar),
1017 retvar,
1018 )
1019}
1020
1021fn render_cffi_fut_ptr_transforms(
1022 reg: &Handlebars,
1023 type_schema: &TypeSchema,
1024 mut transform_block: String,
1025 retvar: &str,
1026) -> Result<String> {
1027 let next_schema = match type_schema.ty.as_str() {
1028 "Option" => {
1029 let transform = format!(
1030 "{} as *const *const c_void;\nlet {} = *unsafe {{ {}.as_ref().unwrap() }};\nlet {} = if {}.is_null() {{\nNone\n}} else {{\nSome({{{{{{next}}}}}})\n}};\n",
1031 retvar, retvar, retvar, retvar, retvar,
1032 );
1033 transform_block = reg.render_template(&transform_block, &json!({"next": transform}))?;
1034
1035 type_schema.generic_ty_args.get(0)
1036 }
1037 "AnyArc" => {
1038 let transform = format!("Arc::new(SafePtr({})) as AnyArc", retvar);
1039 transform_block = reg.render_template(&transform_block, &json!({"next": transform}))?;
1040
1041 type_schema.generic_ty_args.get(0)
1042 }
1043 "Arc" => {
1044 let transform = format!("Arc::new(SafePtr({}))", retvar);
1045 transform_block = reg.render_template(&transform_block, &json!({"next": transform}))?;
1046
1047 type_schema.generic_ty_args.get(0)
1048 }
1049 "SafePtr" => type_schema.generic_ty_args.get(0),
1050 ty => {
1051 bail!("Unsupported type for return type transform: {}", ty);
1052 }
1053 };
1054
1055 if let Some(inner_schema) = next_schema {
1056 render_cffi_fut_ptr_transforms(reg, inner_schema, transform_block, retvar)
1057 } else {
1058 Ok(reg.render_template(&transform_block, &json!({"next": retvar}))?)
1059 }
1060}
1061
1062fn render_cffi_c_fn_return_transforms(
1063 reg: &Handlebars,
1064 type_schema: &TypeSchema,
1065 mut transform_block: String,
1066 retvar: &str,
1067) -> Result<String> {
1068 let next_schema = match type_schema.ty.as_str() {
1069 "Option" => {
1070 let transform = format!(
1071 "{}\n .map(|{}| {{{{{{next}}}}}})\n .unwrap_or(std::ptr::null())",
1072 retvar, retvar,
1073 );
1074 transform_block = reg.render_template(&transform_block, &json!({"next": transform}))?;
1075
1076 type_schema.generic_ty_args.get(0)
1077 }
1078 "AnyArc" => {
1079 let transform = format!("{}.downcast::<SafePtr>().unwrap().0", retvar);
1080 transform_block = reg.render_template(&transform_block, &json!({"next": transform}))?;
1081
1082 type_schema.generic_ty_args.get(0)
1083 }
1084 "Arc" => {
1085 let transform = format!("{{let {} = *{};\n{{{{{{next}}}}}}}}", retvar, retvar);
1086 transform_block = reg.render_template(&transform_block, &json!({"next": transform}))?;
1087
1088 type_schema.generic_ty_args.get(0)
1089 }
1090 "SafePtr" => {
1091 let transform = format!("{}.0", retvar);
1092 transform_block = reg.render_template(&transform_block, &json!({"next": transform}))?;
1093
1094 type_schema.generic_ty_args.get(0)
1095 }
1096 ty => {
1097 bail!("Unsupported type for c fn return type transform: {}", ty);
1098 }
1099 };
1100
1101 if let Some(inner_schema) = next_schema {
1102 render_cffi_c_fn_return_transforms(reg, inner_schema, transform_block, retvar)
1103 } else {
1104 Ok(reg.render_template(&transform_block, &json!({"next": retvar}))?)
1105 }
1106}
1107
1108fn trait_schema_to_from_impl(trait_schema: &TraitSchema) -> Result<FunctionSchema> {
1110 Ok(FunctionSchema {
1111 name: "from".to_string(),
1112 args: vec![FunctionArgSchema {
1113 name: "inner".to_string(),
1114 ty: Some(TypeSchema {
1115 ty: format!("&dyn {}", get_trait_name_w_subbed_generics(trait_schema)?),
1116 generic_ty_args: vec![],
1117 }),
1118 annotations: None,
1119 }],
1120 return_type: TypeSchema {
1121 ty: "Self".to_string(),
1122 generic_ty_args: vec![],
1123 },
1124 body: Some(trait_schema_to_from_impl_body(trait_schema)?),
1125 extern_layout: None,
1126 annotations: None,
1127 })
1128}
1129
1130fn trait_schema_to_from_impl_body(trait_schema: &TraitSchema) -> Result<String> {
1132 let mut fn_body = String::new();
1133
1134 writeln!(fn_body, "Cffi{} {{", trait_schema.name).unwrap();
1135
1136 writeln!(
1138 fn_body,
1139 " // Wrap the fat pointer in a Box to get a stable address"
1140 )
1141 .unwrap();
1142 writeln!(fn_body, " // Leak it to keep [just the wrapper] alive").unwrap();
1143 writeln!(
1144 fn_body,
1145 " self_ptr: Box::into_raw(Box::new(inner)) as *const c_void,"
1146 )
1147 .unwrap();
1148
1149 for supertrait in trait_schema
1151 .supertraits
1152 .iter()
1153 .filter(|st| st.ty != "Send" && st.ty != "Sync")
1154 {
1155 writeln!(fn_body, " cffi_{}: {{\n", supertrait.ty.to_snake_case()).unwrap();
1156 writeln!(
1157 fn_body,
1158 " let {} = Box::new(inner as &dyn {});",
1159 supertrait.ty.to_snake_case(),
1160 supertrait.ty
1161 )
1162 .unwrap();
1163 writeln!(
1164 fn_body,
1165 " let ret: Cffi{} = (*{}).into();",
1166 supertrait.ty,
1167 supertrait.ty.to_snake_case()
1168 )
1169 .unwrap();
1170 writeln!(
1171 fn_body,
1172 " Box::leak({}); // Leak to keep alive",
1173 supertrait.ty.to_snake_case()
1174 )
1175 .unwrap();
1176 writeln!(fn_body, " ret").unwrap();
1177 writeln!(fn_body, " }},").unwrap();
1178 }
1179
1180 for func in &trait_schema.functions {
1182 writeln!(
1183 fn_body,
1184 " {}_fut: Cffi{}::{}_fut_impl,",
1185 func.name, trait_schema.name, func.name
1186 )
1187 .unwrap();
1188 }
1189
1190 writeln!(fn_body, "}}").unwrap();
1191
1192 Ok(fn_body)
1193}
1194
1195#[cfg(test)]
1197mod tests {
1198 use crate::cffi_type_utils::CffiTypeElementSpec;
1199 use rs_schema::FnArgAnnotations;
1200
1201 use super::*;
1202
1203 #[test]
1205 fn test_trait_to_async_cffi_schema_with_no_functions() {
1206 let trait_schema = TraitSchema {
1207 name: "TestTrait".to_string(),
1208 functions: vec![],
1209 generics: vec![],
1210 supertraits: vec![],
1211 };
1212
1213 let result = trait_to_async_cffi_schema(&trait_schema, &HashMap::new()).unwrap();
1214
1215 assert_eq!(result.structs.len(), 1);
1217 assert_eq!(result.structs[0].name, "CffiTestTrait");
1218 assert_eq!(result.structs[0].fields.len(), 1);
1220 assert_eq!(result.structs[0].fields[0].name, "self_ptr");
1221
1222 assert_eq!(result.trait_impls.len(), 5);
1224 assert_eq!(result.trait_impls[0].trait_name, "TestTrait");
1225 assert_eq!(
1226 result.trait_impls[0].impl_for,
1227 Some("CffiTestTrait".to_string())
1228 );
1229 assert!(!result.trait_impls[0].unsafe_impl);
1230
1231 assert_eq!(result.trait_impls[1].trait_name, "Send");
1232 assert!(result.trait_impls[1].unsafe_impl);
1233
1234 assert_eq!(result.trait_impls[2].trait_name, "Sync");
1235 assert!(result.trait_impls[2].unsafe_impl);
1236 }
1237
1238 #[test]
1240 fn test_trait_to_async_cffi_schema_with_functions() {
1241 let trait_schema = TraitSchema {
1242 name: "MyTrait".to_string(),
1243 functions: vec![
1244 FunctionSchema {
1245 name: "method_one".to_string(),
1246 args: vec![FunctionArgSchema {
1247 name: "arg1".to_string(),
1248 ty: Some(TypeSchema {
1249 ty: "i32".to_string(),
1250 generic_ty_args: vec![],
1251 }),
1252 annotations: None,
1253 }],
1254 return_type: TypeSchema {
1255 ty: "BoxFuture".to_string(),
1256 generic_ty_args: vec![
1257 TypeSchema::new_simple("'_".to_string()),
1258 TypeSchema {
1259 ty: "Option".to_string(),
1260 generic_ty_args: vec![TypeSchema::new_simple("AnyArc".to_string())],
1261 },
1262 ],
1263 },
1264 body: None,
1265 extern_layout: None,
1266 annotations: None,
1267 },
1268 FunctionSchema {
1269 name: "method_two".to_string(),
1270 args: vec![],
1271 return_type: TypeSchema {
1272 ty: "BoxFuture".to_string(),
1273 generic_ty_args: vec![
1274 TypeSchema::new_simple("'_".to_string()),
1275 TypeSchema::new_simple("()".to_string()),
1276 ],
1277 },
1278 body: None,
1279 extern_layout: None,
1280 annotations: None,
1281 },
1282 ],
1283 generics: vec![],
1284 supertraits: vec![],
1285 };
1286
1287 let result = trait_to_async_cffi_schema(&trait_schema, &HashMap::new()).unwrap();
1288
1289 assert_eq!(result.structs.len(), 1);
1291 assert_eq!(result.structs[0].name, "CffiMyTrait");
1292 assert_eq!(result.structs[0].fields.len(), 3);
1294 assert_eq!(result.structs[0].fields[0].name, "self_ptr");
1295 assert_eq!(result.structs[0].fields[1].name, "method_one_fut");
1296 assert_eq!(result.structs[0].fields[2].name, "method_two_fut");
1297
1298 assert_eq!(
1300 result.structs[0].fields[1].field_type.ty,
1301 "extern \"C\" fn(*const c_void) -> *const c_void"
1302 );
1303 assert_eq!(
1304 result.structs[0].fields[2].field_type.ty,
1305 "extern \"C\" fn() -> *const c_void"
1306 );
1307
1308 assert_eq!(result.trait_impls.len(), 5);
1310 assert_eq!(result.trait_impls[0].trait_name, "MyTrait");
1311 assert_eq!(result.trait_impls[0].functions.len(), 2);
1312 assert_eq!(result.trait_impls[0].functions[0].name, "method_one");
1313 assert_eq!(result.trait_impls[0].functions[1].name, "method_two");
1314 }
1315
1316 #[test]
1318 fn test_fn_arg_to_transforms_basic() {
1319 let arg_schema = FunctionArgSchema {
1320 name: "arg1".to_string(),
1321 ty: Some(TypeSchema {
1322 ty: "Option".to_string(),
1323 generic_ty_args: vec![TypeSchema::new_simple("AnyArc".to_string())],
1324 }),
1325 annotations: None,
1326 };
1327
1328 fn_arg_to_transforms(&arg_schema).unwrap();
1329 }
1330
1331 #[test]
1333 fn test_fn_arg_to_transforms_with_type_annotation() {
1334 let arg_schema = FunctionArgSchema {
1335 name: "arg1".to_string(),
1336 ty: Some(TypeSchema {
1337 ty: "Vec".to_string(),
1338 generic_ty_args: vec![TypeSchema {
1339 ty: "Option".to_string(),
1340 generic_ty_args: vec![TypeSchema::new_simple("AnyArc".to_string())],
1341 }],
1342 }),
1343 annotations: Some({
1344 let mut annotations = FnArgAnnotations::new();
1345 annotations.cffi_type = Some("CffiPointerBuffer<opt_ptr<T>>".to_string());
1346 annotations
1347 }),
1348 };
1349
1350 fn_arg_to_transforms(&arg_schema).unwrap();
1351 }
1352
1353 #[test]
1355 fn test_fn_arg_to_transforms_arc() {
1356 let arg_schema = FunctionArgSchema {
1357 name: "arg1".to_string(),
1358 ty: Some(TypeSchema {
1359 ty: "Arc".to_string(),
1360 generic_ty_args: vec![TypeSchema::new_simple("SomeType".to_string())],
1361 }),
1362 annotations: None,
1363 };
1364
1365 let out = fn_arg_to_transforms(&arg_schema).unwrap();
1366 let out = out.transforms.unwrap();
1367 assert!(out.contains("Arc::into_raw"));
1368 assert!(out.contains("SafePtr("));
1369 }
1370
1371 #[test]
1373 fn test_fn_arg_to_transforms_anyarc() {
1374 let arg_schema = FunctionArgSchema {
1375 name: "arg1".to_string(),
1376 ty: Some(TypeSchema::new_simple("AnyArc".to_string())),
1377 annotations: None,
1378 };
1379
1380 let out = fn_arg_to_transforms(&arg_schema).unwrap();
1381 let out = out.transforms.unwrap();
1382 assert!(out.contains("arg1.downcast::<SafePtr>()"));
1383 assert!(out.contains("let arg1 ="));
1384 }
1385
1386 #[test]
1388 fn test_fn_arg_to_transforms_option_arc() {
1389 let arg_schema = FunctionArgSchema {
1390 name: "arg1".to_string(),
1391 ty: Some(TypeSchema {
1392 ty: "Option".to_string(),
1393 generic_ty_args: vec![TypeSchema {
1394 ty: "Arc".to_string(),
1395 generic_ty_args: vec![TypeSchema::new_simple("T".to_string())],
1396 }],
1397 }),
1398 annotations: None,
1399 };
1400
1401 let out = fn_arg_to_transforms(&arg_schema).unwrap();
1402 let out = out.transforms.unwrap();
1403 assert!(out.contains("if let Some(arg1) = arg1"));
1404 assert!(out.contains("Arc::into_raw"));
1405 }
1406
1407 #[test]
1409 fn test_type_transforms_for_arg_vec() {
1410 let arg_schema = FunctionArgSchema {
1411 name: "data".to_string(),
1412 ty: Some(TypeSchema {
1413 ty: "Vec".to_string(),
1414 generic_ty_args: vec![TypeSchema::new_simple("i32".to_string())],
1415 }),
1416 annotations: None,
1417 };
1418
1419 let out = type_transforms_for_arg(&arg_schema).unwrap();
1420 let out = out.transforms;
1421 assert!(out.is_some());
1423 let transform = out.unwrap();
1424 assert!(transform.contains("let data = "));
1425 }
1426
1427 #[test]
1429 fn test_codegen_struct_indentation() {
1430 let struct_schema = RsStructSchema {
1431 macros: vec!["#[repr(C)]".to_string()],
1432 name: "Example".to_string(),
1433 fields: vec![RsFieldSchema {
1434 name: "value".to_string(),
1435 field_type: TypeSchema::new_simple("i32".to_string()),
1436 }],
1437 };
1438
1439 let code = struct_schema.codegen(1);
1440 let lines: Vec<_> = code.lines().collect();
1441 assert!(lines.iter().all(|line| line.starts_with(" ")));
1442 assert!(code.contains("pub struct Example"));
1443 assert!(code.contains("pub value: i32"));
1444 }
1445
1446 #[test]
1448 fn test_codegen_trait_impl_indentation() {
1449 let function_schema = FunctionSchema {
1450 name: "example".to_string(),
1451 args: vec![],
1452 return_type: TypeSchema::new_simple("()".to_string()),
1453 body: Some("".to_string()),
1454 extern_layout: None,
1455 annotations: None,
1456 };
1457
1458 let trait_impl_schema = RsTraitImplSchema {
1459 trait_name: "ExampleTrait".to_string(),
1460 impl_for: Some("Example".to_string()),
1461 functions: vec![function_schema],
1462 generic_args: vec![],
1463 unsafe_impl: false,
1464 comment_out: false,
1465 };
1466
1467 let code = trait_impl_schema.codegen(1);
1468 let mut lines = code.lines();
1469 assert!(lines.next().unwrap().starts_with(" impl"));
1470 assert!(lines.any(|line| line.starts_with(" ")));
1471 }
1472
1473 #[test]
1475 fn test_rs_file_schema_codegen_uses_children() {
1476 let struct_schema = RsStructSchema {
1477 macros: vec![],
1478 name: "Example".to_string(),
1479 fields: vec![],
1480 };
1481
1482 let file_schema = RsFileSchema {
1483 structs: vec![struct_schema],
1484 trait_impls: vec![],
1485 };
1486
1487 let code = file_schema.codegen(0);
1488 assert!(code.contains("autogenerated"));
1489 assert!(code.contains("pub struct Example"));
1490 }
1491
1492 #[test]
1494 fn test_fn_arg_to_async_cffi_type_spec_none() {
1495 let arg = FunctionArgSchema {
1496 name: "arg0".to_string(),
1497 ty: None,
1498 annotations: None,
1499 };
1500
1501 let spec = fn_arg_to_async_cffi_type_spec(&arg).unwrap();
1502 assert_eq!(spec.len(), 2);
1503 assert!(spec[0].is_pointer);
1504 assert!(!spec[1].is_pointer);
1505 }
1506
1507 #[test]
1509 fn test_cffi_type_str_to_type_stack_mappings() {
1510 let s = "CffiPointerBuffer<opt_ptr<ptr>>".to_string();
1511 let stack = cffi_type_str_to_type_stack(&s).unwrap();
1512 assert!(stack.len() >= 3);
1514 assert!(stack[0].is_pointer);
1515 assert_eq!(stack[0].explicit_type.as_deref(), Some("CffiPointerBuffer"));
1516 assert!(stack[1].is_pointer && stack[1].is_optional);
1518 assert!(stack[2].is_pointer && !stack[2].is_optional);
1520 }
1521
1522 #[test]
1524 fn test_annotation_transforms_for_arg_mismatch_errors() {
1525 let mut annotations = FnArgAnnotations::new();
1526 annotations.cffi_type = Some("ptr".to_string());
1527
1528 let arg = FunctionArgSchema {
1529 name: "a".to_string(),
1530 ty: Some(TypeSchema::new_simple("i32".to_string())),
1531 annotations: Some(annotations),
1532 };
1533
1534 let res = fn_arg_to_transforms(&arg);
1536 assert!(res.is_err());
1537 }
1538
1539 #[test]
1541 fn test_async_block_for_trait_fn_structure_and_call_args() {
1542 let func = FunctionSchema {
1543 name: "do_something".to_string(),
1544 args: vec![
1545 FunctionArgSchema {
1546 name: "self".to_string(),
1547 ty: None,
1548 annotations: None,
1549 },
1550 FunctionArgSchema {
1551 name: "x".to_string(),
1552 ty: None,
1553 annotations: None,
1554 },
1555 ],
1556 return_type: TypeSchema {
1557 ty: "BoxFuture".to_string(),
1558 generic_ty_args: vec![
1559 TypeSchema::new_simple("'_".to_string()),
1560 TypeSchema::new_simple("()".to_string()),
1561 ],
1562 },
1563 body: None,
1564 extern_layout: None,
1565 annotations: None,
1566 };
1567
1568 let block = async_block_for_trait_fn(&func, vec![true, false], &vec![]).unwrap();
1569 assert!(block.contains("Box::pin(async move"));
1570 assert!(block.contains("fut.await"));
1571 assert!(block.contains("(do_something_fn)(self_ptr.0, x);"));
1573 }
1574
1575 #[test]
1577 fn test_arg_to_field_type_vec_produces_cffi_pointerbuffer() {
1578 let arg = FunctionArgSchema {
1579 name: "data".to_string(),
1580 ty: Some(TypeSchema {
1581 ty: "Vec".to_string(),
1582 generic_ty_args: vec![TypeSchema::new_simple("i32".to_string())],
1583 }),
1584 annotations: None,
1585 };
1586
1587 let t = arg_to_field_type(&arg).unwrap();
1588 assert_eq!(t.ty, "CffiPointerBuffer");
1589 }
1590
1591 #[test]
1593 fn test_trait_fn_to_cffi_c_fn_body_errors_on_non_boxfuture() {
1594 let func = FunctionSchema {
1595 name: "bad_return".to_string(),
1596 args: vec![FunctionArgSchema {
1597 name: "self".to_string(),
1598 ty: None,
1599 annotations: None,
1600 }],
1601 return_type: TypeSchema::new_simple("String".to_string()),
1602 body: None,
1603 extern_layout: None,
1604 annotations: None,
1605 };
1606
1607 let trait_schema = TraitSchema {
1608 name: "MyTrait".to_string(),
1609 functions: vec![],
1610 generics: vec![],
1611 supertraits: vec![],
1612 };
1613
1614 let err = trait_fn_to_cffi_c_fn_body(&func, &trait_schema, &vec![]).unwrap_err();
1615 assert!(err.to_string().contains("BoxFuture"));
1616 }
1617
1618 #[test]
1620 fn test_type_transforms_for_cffi_c_fn_return_option_anyarc() {
1621 let ty_schema = TypeSchema {
1622 ty: "Option".to_string(),
1623 generic_ty_args: vec![TypeSchema::new_simple("AnyArc".to_string())],
1624 };
1625
1626 let transform = type_transforms_for_cffi_c_fn_return(&ty_schema, "ret").unwrap();
1627
1628 assert!(transform.contains("map(|ret|"));
1629 assert!(transform.contains("downcast::<SafePtr>().unwrap().0"));
1630 assert!(transform.contains("unwrap_or(std::ptr::null())"));
1631 }
1632
1633 #[test]
1635 fn test_cffi_c_fn_arg_to_transforms_for_pointer_buffer() {
1636 let mut annotations = FnArgAnnotations::new();
1637 annotations.cffi_type = Some("CffiPointerBuffer".to_string());
1638
1639 let arg = FunctionArgSchema {
1640 name: "values".to_string(),
1641 ty: Some(TypeSchema {
1642 ty: "Vec".to_string(),
1643 generic_ty_args: vec![TypeSchema {
1644 ty: "Option".to_string(),
1645 generic_ty_args: vec![TypeSchema::new_simple("AnyArc".to_string())],
1646 }],
1647 }),
1648 annotations: Some(annotations),
1649 };
1650
1651 let trait_schema = TraitSchema {
1652 name: "Trait".to_string(),
1653 functions: vec![],
1654 generics: vec![],
1655 supertraits: vec![],
1656 };
1657
1658 let transforms = cffi_c_fn_arg_to_transforms(&arg, &trait_schema).unwrap();
1659 assert!(transforms.contains(".as_slice()"));
1660 assert!(transforms.contains("SafePtr"));
1661 }
1662
1663 #[test]
1665 fn test_cffi_c_fn_arg_to_transforms_for_self_ptr() {
1666 let arg = FunctionArgSchema {
1667 name: "self".to_string(),
1668 ty: None,
1669 annotations: None,
1670 };
1671
1672 let trait_schema = TraitSchema {
1673 name: "Trait".to_string(),
1674 functions: vec![],
1675 generics: vec![],
1676 supertraits: vec![],
1677 };
1678
1679 let transforms = cffi_c_fn_arg_to_transforms(&arg, &trait_schema).unwrap();
1680 assert!(transforms.contains("Self pointer cannot be null"));
1681 assert!(transforms.contains("dyn Trait + Send + Sync"));
1682 }
1683
1684 #[test]
1686 fn test_trait_schema_to_from_impl_includes_function_fields() {
1687 let trait_schema = TraitSchema {
1688 name: "ExampleTrait".to_string(),
1689 functions: vec![FunctionSchema {
1690 name: "do_it".to_string(),
1691 args: vec![],
1692 return_type: TypeSchema {
1693 ty: "BoxFuture".to_string(),
1694 generic_ty_args: vec![
1695 TypeSchema::new_simple("'_".to_string()),
1696 TypeSchema::new_simple("()".to_string()),
1697 ],
1698 },
1699 body: None,
1700 extern_layout: None,
1701 annotations: None,
1702 }],
1703 generics: vec![],
1704 supertraits: vec![],
1705 };
1706
1707 let from_impl = trait_schema_to_from_impl(&trait_schema).unwrap();
1708 let body = from_impl.body.unwrap();
1709
1710 assert!(body.contains("CffiExampleTrait"));
1711 assert!(body.contains("do_it_fut_impl"));
1712 assert!(body.contains("Box::into_raw"));
1713 }
1714
1715 #[test]
1717 fn test_cffi_ty_stack_to_rust_ty_cases() {
1718 let stack = vec![CffiTypeElementSpec {
1720 is_pointer: false,
1721 is_optional: false,
1722 explicit_type: Some("CffiX".to_string()),
1723 }];
1724 let out = cffi_ty_stack_to_rust_cffi_ty(&stack).unwrap();
1725 assert_eq!(out, "CffiX");
1726
1727 let stack2 = vec![CffiTypeElementSpec {
1729 is_pointer: true,
1730 is_optional: false,
1731 explicit_type: None,
1732 }];
1733 let out2 = cffi_ty_stack_to_rust_cffi_ty(&stack2).unwrap();
1734 assert_eq!(out2, "SafePtr");
1735
1736 let empty: Vec<CffiTypeElementSpec> = vec![];
1738 assert!(cffi_ty_stack_to_rust_cffi_ty(&empty).is_err());
1739 }
1740
1741 #[test]
1743 fn test_substitute_generic_args_no_generics() {
1744 let ty = TypeSchema {
1745 ty: "i32".to_string(),
1746 generic_ty_args: vec![],
1747 };
1748
1749 let out = substitute_generic_args(&ty, &vec![]).unwrap();
1750 assert_eq!(out.ty, "i32");
1751 assert!(out.generic_ty_args.is_empty());
1752 }
1753
1754 #[test]
1756 fn test_get_trait_name_w_subbed_generics_simple() {
1757 let trait_schema = TraitSchema {
1758 name: "SimpleTrait".to_string(),
1759 functions: vec![],
1760 generics: vec![],
1761 supertraits: vec![],
1762 };
1763
1764 let out = get_trait_name_w_subbed_generics(&trait_schema).unwrap();
1765 assert_eq!(out, "SimpleTrait");
1766 }
1767
1768 #[test]
1770 fn test_type_transforms_for_cffi_fut_ptr_option_arc() {
1771 let ty_schema = TypeSchema {
1772 ty: "Option".to_string(),
1773 generic_ty_args: vec![TypeSchema {
1774 ty: "Arc".to_string(),
1775 generic_ty_args: vec![TypeSchema::new_simple("SafePtr".to_string())],
1776 }],
1777 };
1778 let transform = type_transforms_for_cffi_fut_ptr(&ty_schema, "ret").unwrap();
1779 assert!(
1780 transform.contains("Arc::new(SafePtr(ret))")
1781 || transform.contains("Arc::new(SafePtr({ret}))")
1782 );
1783 assert!(transform.contains("as_ref().unwrap") || transform.contains("as_ref()"));
1784 }
1785
1786 #[test]
1788 fn test_type_transforms_for_cffi_fut_ptr_unsupported_type() {
1789 let ty_schema = TypeSchema::new_simple("String".to_string());
1790 let res = type_transforms_for_cffi_fut_ptr(&ty_schema, "ret");
1791 assert!(res.is_err());
1792 assert!(res.err().unwrap().to_string().contains("Unsupported type"));
1793 }
1794
1795 #[test]
1797 fn test_cffi_c_fn_arg_to_transforms_c_ulong() {
1798 let arg = FunctionArgSchema {
1799 name: "n".to_string(),
1800 ty: Some(TypeSchema::new_simple("usize".to_string())),
1801 annotations: None,
1802 };
1803
1804 let trait_schema = TraitSchema {
1805 name: "Trait".to_string(),
1806 functions: vec![],
1807 generics: vec![],
1808 supertraits: vec![],
1809 };
1810
1811 let t = cffi_c_fn_arg_to_transforms(&arg, &trait_schema).unwrap();
1812 assert!(t.contains("as usize") || t.contains("as usize;"));
1813 }
1814
1815 #[test]
1817 fn test_annotation_transforms_for_arg_collection_as_item() {
1818 let mut annotations = FnArgAnnotations::new();
1819 annotations.collection_as_item = true;
1820
1821 let arg = FunctionArgSchema {
1822 name: "it".to_string(),
1823 ty: Some(TypeSchema {
1824 ty: "Vec".to_string(),
1825 generic_ty_args: vec![TypeSchema::new_simple("AnyArc".to_string())],
1826 }),
1827 annotations: Some(annotations),
1828 };
1829
1830 let out = annotation_transforms_for_arg(&arg, vec![]).unwrap();
1831 assert!(out.is_some());
1832 let s = out.unwrap();
1833 assert!(s.contains("into_iter().next().unwrap()"));
1834 }
1835
1836 #[test]
1838 fn test_type_transforms_for_arg_box_dyn() {
1839 let arg = FunctionArgSchema {
1840 name: "b".to_string(),
1841 ty: Some(TypeSchema {
1842 ty: "Box".to_string(),
1843 generic_ty_args: vec![TypeSchema {
1844 ty: "dyn SomeTrait".to_string(),
1845 generic_ty_args: vec![],
1846 }],
1847 }),
1848 annotations: None,
1849 };
1850
1851 let out = type_transforms_for_arg(&arg).unwrap();
1852 assert!(!out.returns_safe_ptr);
1853 let t = out.transforms.unwrap();
1854 assert!(t.contains("Box::leak") || t.contains("Box::leak("));
1855 assert!(t.contains("cffi_b") || t.contains("cffi_b"));
1856 }
1857}