1use std::collections::hash_map::Entry;
19use std::collections::{HashMap, HashSet};
20use std::ops::Deref;
21
22use anyhow::{anyhow, Context};
23use proc_macro2::{Ident, Span};
24use quote::ToTokens;
25use syn::punctuated::Punctuated;
26use syn::{
27 parse_quote,
28 FnArg,
29 ForeignItem,
30 ForeignItemFn,
31 ForeignItemType,
32 GenericArgument,
33 ItemEnum,
34 ItemForeignMod,
35 Pat,
36 PatIdent,
37 PathArguments,
38 PathSegment,
39 Receiver,
40 ReturnType,
41 Token,
42 TraitBound,
43 Type,
44 TypeParamBound,
45 TypeReference,
46 TypeTraitObject,
47};
48
49pub use crate::binding_types::*;
50use crate::cpp::exception_class_name;
51use crate::cpp::templates::TargetLanguageTypeName;
52use crate::enum_helpers::is_primitive_enum;
53use crate::ordered_hash_set::OrderedHashSet;
54use crate::utils::{is_primitive, BuildContext};
55
56pub struct ExternModuleTranslator {
66 pub shared_enums: HashSet<ItemEnum>,
67 pub exception_names: HashSet<String>,
68 pub rust_types_wrappers: OrderedHashSet<WrapperType>,
69 pub user_custom_types: HashMap<WrapperType, Vec<Function>>,
70 pub user_traits: HashMap<WrapperType, Vec<Function>>,
71 pub global_functions: Vec<Function>,
72 pub exception_trait_methods: HashSet<Function>,
73 pub declared_impls: HashMap<String, Vec<String>>,
74}
75
76impl ExternModuleTranslator {
77 pub fn new(shared_enums: HashSet<ItemEnum>) -> Self {
78 let mut rust_types_wrappers = OrderedHashSet::default();
79 shared_enums
80 .iter()
81 .for_each(|enum_item| rust_types_wrappers.insert(parse_enum_wrapper(enum_item)));
82
83 Self {
84 shared_enums,
85 rust_types_wrappers,
86 exception_names: Default::default(),
87 user_custom_types: Default::default(),
88 user_traits: Default::default(),
89 global_functions: Default::default(),
90 exception_trait_methods: Default::default(),
91 declared_impls: Default::default(),
92 }
93 }
94
95 pub fn parse_items_in_extern_rust_module(
100 &mut self,
101 extern_module: &ItemForeignMod,
102 context: &BuildContext,
103 ) -> anyhow::Result<()> {
104 extern_module
105 .items
106 .iter()
107 .filter(|extern_item| match extern_item {
108 ForeignItem::Fn(function) => context.check_cfg_attrs(&function.attrs),
109 ForeignItem::Type(type_) => context.check_cfg_attrs(&type_.attrs),
110 _ => true,
111 })
112 .try_for_each(|extern_item| match extern_item {
113 ForeignItem::Fn(function) => self.register_new_rust_function(function),
114 ForeignItem::Type(ForeignItemType { ident, .. }) => {
115 self.register_custom_type(ident);
116 Ok(())
117 }
118 ForeignItem::Verbatim(ts) => {
119 if let Some(token) = ts
120 .clone()
121 .into_iter()
122 .skip_while(|token| token.to_string().to_lowercase() != "type")
123 .skip(1)
124 .take(1)
125 .next()
126 {
127 let type_name = token.to_string();
128 struct FoldedTokens {
129 tokens: Vec<String>,
130 curr: String,
131 }
132 let traits = ts
133 .clone()
134 .into_iter()
135 .skip_while(|token| token.to_string() != ":")
136 .skip(1)
137 .fold(
138 FoldedTokens {
139 tokens: vec![],
140 curr: String::new(),
141 },
142 |mut tn, token| match token {
143 proc_macro2::TokenTree::Ident(ident) => {
144 tn.curr.push_str(ident.to_string().as_str());
145 tn
146 }
147 proc_macro2::TokenTree::Punct(punct) => match punct.as_char() {
148 '>' => {
149 tn.curr.push('>');
150 tn.tokens.push(tn.curr);
151 tn.curr = String::new();
152 tn
153 }
154 '+' => {
155 tn.tokens.push(tn.curr);
156 tn.curr = String::new();
157 tn
158 }
159 ';' | '=' => {
160 tn.tokens.push(tn.curr.to_owned());
161 tn.curr = String::new();
162 tn
163 }
164 c => {
165 tn.curr.push(c);
166 tn
167 }
168 },
169 _ => tn,
170 },
171 );
172 self.declared_impls.insert(
173 type_name.to_owned(),
174 traits
175 .tokens
176 .into_iter()
177 .filter(|t| !t.is_empty())
178 .collect::<Vec<String>>(),
179 );
180 self.register_custom_type(&Ident::new(&type_name, Span::call_site()));
181 }
182 Ok(())
183 }
184
185 _ => Ok(()),
186 })
187 }
188
189 pub fn translate_trait_external_modules(
194 &mut self,
195 extern_module: &ItemForeignMod,
196 context: &BuildContext,
197 ) -> anyhow::Result<()> {
198 extern_module
199 .items
200 .iter()
201 .filter(|extern_item| match extern_item {
202 ForeignItem::Fn(function) => context.check_cfg_attrs(&function.attrs),
203 _ => true,
204 })
205 .try_for_each(|extern_item| match extern_item {
206 ForeignItem::Fn(original_function) => {
207 let (associated_structure, function) =
208 self.translate_function(original_function)?;
209 if let Some(custom_trait) = associated_structure {
210 self.user_traits
211 .entry(custom_trait)
212 .or_default()
213 .push(function);
214 Ok(())
215 } else {
216 Err(anyhow!(
217 "Global functions are not supported in extern \"Traits\"."
218 ))
219 }
220 }
221 _ => Ok(()),
222 })
223 }
224
225 pub fn translate_exception_trait_external_module(
230 &mut self,
231 extern_module: &ItemForeignMod,
232 context: &BuildContext,
233 ) -> anyhow::Result<()> {
234 extern_module
235 .items
236 .iter()
237 .filter(|extern_item| match extern_item {
238 ForeignItem::Fn(function) => context.check_cfg_attrs(&function.attrs),
239 _ => true,
240 })
241 .try_for_each(|extern_item| match extern_item {
242 ForeignItem::Fn(function) => {
243 let method = self.translate_exception_trait_function(function)?;
244 self.exception_trait_methods.insert(method);
245 Ok(())
246 }
247 _ => Err(anyhow!(
248 "Only functions are acceptable in `ExceptionsTrait`"
249 )),
250 })
251 }
252
253 fn register_custom_type(&mut self, original_type_name: &Ident) -> WrapperType {
259 let new_wrapper_type = WrapperType {
260 original_type_name: parse_quote!( #original_type_name ),
261 wrapper_name: original_type_name.to_string(),
262 rust_type: RustWrapperType::Custom,
263 reference_parameters: None,
264 impl_traits: vec![],
265 };
266 self.rust_types_wrappers.insert(new_wrapper_type.clone());
267
268 if let Entry::Vacant(entry) = self.user_custom_types.entry(new_wrapper_type.clone()) {
269 entry.insert(vec![]);
270 };
271 new_wrapper_type
272 }
273
274 pub fn translate_function(
342 &mut self,
343 function: &ForeignItemFn,
344 ) -> anyhow::Result<(Option<WrapperType>, Function)> {
345 let mut arguments = vec![];
346 let mut associated_structure = None;
347
348 function
349 .sig
350 .inputs
351 .iter()
352 .try_for_each(|argument| match argument {
353 FnArg::Typed(argument) => {
354 let new_wrapper_type = self
355 .parse_and_register_rust_type(&argument.ty)
356 .with_context(|| {
357 format!(
358 "Unsupported type in function signature: {}",
359 function.sig.ident
360 )
361 })?;
362 if new_wrapper_type.rust_type == RustWrapperType::DataEnum {
364 return Err(anyhow!(
365 "Data carrying enums as functions arguments are not supported yet!"
366 ));
367 };
368 if let Pat::Ident(PatIdent { ident, .. }) = argument.pat.as_ref() {
369 arguments.push(Arg {
370 arg_name: ident.to_string(),
371 typ: new_wrapper_type,
372 });
373 }
374 Ok(())
375 }
376 FnArg::Receiver(argument) => {
377 let new_wrapper_type = self
378 .parse_and_register_rust_type(&argument.ty)
379 .with_context(|| {
380 format!(
381 "Unsupported type in function signature: {}",
382 function.sig.ident
383 )
384 })?;
385 if new_wrapper_type.rust_type == RustWrapperType::DataEnum {
387 return Err(anyhow!(
388 "Data carrying enums as functions arguments are not supported yet!"
389 ));
390 };
391
392 arguments.push(Arg {
393 arg_name: "self".to_string(),
394 typ: new_wrapper_type.clone(),
395 });
396 associated_structure = Some(new_wrapper_type);
397
398 Ok(())
399 }
400 })?;
401 let return_type = self.translate_return_type(function)?;
402 let function_name = function.sig.ident.clone();
403 Ok((
404 associated_structure,
405 Function {
406 arguments,
407 return_type,
408 name: function_name.to_string(),
409 },
410 ))
411 }
412
413 fn translate_exception_trait_function(
414 &mut self,
415 function: &ForeignItemFn,
416 ) -> anyhow::Result<Function> {
417 if function.sig.inputs.len() != 1 {
418 Err(anyhow!(
419 "Methods of ExceptionTrait must have only one argument: &self"
420 ))
421 } else if let FnArg::Receiver(Receiver {
422 reference: Some((_, None)),
423 ..
424 }) = function.sig.inputs.first().unwrap()
425 {
426 let return_type = self.translate_return_type(function)?;
427 let associated_structure = WrapperType {
428 original_type_name: parse_quote! {ExceptionTrait},
429 wrapper_name: "ExceptionTrait".to_string(),
430 rust_type: RustWrapperType::ExceptionTrait,
431 reference_parameters: Some(ReferenceParameters::shared()),
432 impl_traits: vec![],
433 };
434 self.rust_types_wrappers
435 .insert(associated_structure.clone());
436 Ok(Function {
437 arguments: vec![Arg {
438 arg_name: "self".to_owned(),
439 typ: associated_structure,
440 }],
441 return_type,
442 name: function.sig.ident.to_string(),
443 })
444 } else {
445 Err(anyhow!(
446 "Methods of ExceptionTrait must have argument &self"
447 ))
448 }
449 }
450
451 fn translate_return_type(
452 &mut self,
453 function: &ForeignItemFn,
454 ) -> anyhow::Result<Option<WrapperType>> {
455 Ok(if let ReturnType::Type(_, typ) = &function.sig.output {
456 let new_wrapper_type = self.parse_and_register_rust_type(typ).with_context(|| {
457 format!(
458 "Unsupported return type in function `{}`",
459 function.sig.ident.clone(),
460 )
461 })?;
462 Some(new_wrapper_type)
463 } else {
464 None
465 })
466 }
467
468 fn register_new_rust_function(&mut self, function: &ForeignItemFn) -> anyhow::Result<()> {
473 let (associated_structure, result_function) = self.translate_function(function)?;
474 if let Some(custom_type) = associated_structure {
475 self.user_custom_types
476 .entry(custom_type)
477 .or_default()
478 .push(result_function);
479 } else {
480 self.global_functions.push(result_function)
481 }
482 Ok(())
483 }
484
485 fn get_inner_generic_type(path_segment: &PathSegment) -> anyhow::Result<&Type> {
490 match &path_segment.arguments {
491 PathArguments::AngleBracketed(args) => args
492 .args
493 .first()
494 .with_context(|| format!("Unsupported generic definition: `{path_segment:?}`"))
495 .and_then(|generic_argument| match generic_argument {
496 GenericArgument::Type(typ) => Ok(typ),
497 _ => Err(anyhow!(
498 "Unsupported generic definition: `{path_segment:?}`"
499 )),
500 }),
501 _ => Err(anyhow!(
502 "Expected inner generic type specification: `{path_segment:?}`"
503 )),
504 }
505 }
506
507 fn get_inner_generic_types(path_segment: &PathSegment) -> anyhow::Result<Vec<&Type>> {
512 match &path_segment.arguments {
513 PathArguments::AngleBracketed(args) => args
514 .args
515 .iter()
516 .map(|generic_argument| match generic_argument {
517 GenericArgument::Type(typ) => Ok(typ),
518 _ => Err(anyhow!("Unsupported generic type")),
519 })
520 .collect(),
521 _ => panic!("get_inner_generic_types called in bad context"),
522 }
523 }
524
525 fn translate_trait_wrapper_type(
529 &self,
530 bounds: &Punctuated<TypeParamBound, Token![+]>,
531 reference_parameters: Option<ReferenceParameters>,
532 ) -> anyhow::Result<WrapperType> {
533 bounds
534 .first()
535 .and_then(|trait_token| {
536 if let TypeParamBound::Trait(TraitBound { path, .. }) = trait_token {
537 path.segments.first().map(|path_segment| {
538 let new_wrapper_type = WrapperType {
539 original_type_name: parse_quote!( #path ),
540 wrapper_name: path_segment.ident.to_string(),
541 rust_type: RustWrapperType::Trait,
542 reference_parameters,
543 impl_traits: vec![],
544 };
545 Ok(new_wrapper_type)
546 })
547 } else {
548 Some(Err(anyhow!(
549 "Lifetimes in TraitObject `{bounds:?}` are not supported"
550 )))
551 }
552 })
553 .unwrap_or_else(|| {
554 Err(anyhow!(
555 "Problem occurred during parsing the &dyn type: Empty type `{bounds:?}`"
556 ))
557 })
558 }
559
560 fn parse_rust_exceptions_type(&mut self, typ: &Type) -> anyhow::Result<WrapperType> {
561 match typ {
562 Type::Path(path) => path
563 .path
564 .segments
565 .first()
566 .context("Invalid enum indicating possible exceptions")
567 .and_then(|path_segment| {
568 let enum_ident = &path_segment.ident;
569 let enum_item = self
570 .get_enum(enum_ident.to_string().as_str())
571 .context("Result's 2nd argument must be an enum's ident.")?;
572 let exc_variants = enum_item
573 .variants
574 .iter()
575 .map(|v| v.ident.clone())
576 .collect::<Vec<_>>();
577
578 let exception_wrapper = WrapperType {
579 original_type_name: typ.clone(),
580 wrapper_name: enum_ident.to_string(),
581 rust_type: if is_primitive_enum(enum_item) {
582 RustWrapperType::Exceptions(Exceptions::Primitive(exc_variants.clone()))
583 } else {
584 RustWrapperType::Exceptions(Exceptions::NonPrimitive(
585 exc_variants.clone(),
586 ))
587 },
588 reference_parameters: None,
589 impl_traits: vec![],
590 };
591
592 self.exception_names.extend(
593 exc_variants
594 .iter()
595 .map(|variant_ident| exception_class_name(enum_ident, variant_ident)),
596 );
597 self.rust_types_wrappers.insert(exception_wrapper.clone());
598
599 Ok(exception_wrapper)
600 }),
601 _ => Err(anyhow!("Could not parse exception type")),
602 }
603 }
604
605 fn parse_and_register_rust_type(&mut self, typ: &Type) -> anyhow::Result<WrapperType> {
616 match typ {
617 Type::Ptr(type_ptr) => match type_ptr.elem.deref() {
618 elem @ Type::Path(_) => {
619 let inner_wrapper = self.parse_and_register_rust_type(elem)?;
620
621 Ok(WrapperType {
622 original_type_name: typ.clone(),
623 wrapper_name: format!("{}Ptr", inner_wrapper.get_name()),
624 rust_type: RustWrapperType::Ptr(Box::new(inner_wrapper)),
625 reference_parameters: None,
626 impl_traits: vec![],
627 })
628 }
629 _ => Err(anyhow!("Pointer not supported: {type_ptr:?}")),
630 },
631 Type::Path(path) => {
632 path.path
633 .segments
634 .first()
635 .with_context(|| format!("Unsupported type `{typ:?}`"))
636 .and_then(|path_segment| {
637 let ident_str = path_segment.ident.to_string();
638 let new_wrapper_type = if let Some(enum_item) = self.get_enum(&ident_str) {
639 parse_enum_wrapper(enum_item)
640 } else if is_primitive(&ident_str) {
641 parse_primitive_wrapper(typ.clone(), &ident_str)
642 } else {
643 match ident_str.as_ref() {
644 "Result" => self.parse_result_wrapper(typ.clone(), path_segment)?,
645 "Option" => self.parse_option_wrapper(typ.clone(), path_segment)?,
646 "Box" => self.parse_box_wrapper(path_segment)?,
647 "Vec" => self.parse_vec_wrapper(typ.clone(), path_segment)?,
648 "Arc" => parse_arc_wrapper(typ.clone(), path_segment)?,
649 "String" => parse_string_wrapper(typ.clone()),
650 _ => parse_custom_wrapper(typ.clone(), path_segment),
651 }
652 };
653
654 if let RustWrapperType::Vector(inner_type) = &new_wrapper_type.rust_type {
657 let inner_type_original = &inner_type.original_type_name;
658 let option_wrapper = WrapperType {
659 original_type_name: parse_quote! { Option<#inner_type_original> },
660 wrapper_name: format!("Optional{}", &inner_type.wrapper_name),
661 rust_type: RustWrapperType::Option(inner_type.clone().boxed()),
662 reference_parameters: None,
663 impl_traits: vec![],
664 };
665 self.rust_types_wrappers.insert(option_wrapper);
666 }
667
668 self.rust_types_wrappers.insert(new_wrapper_type.clone());
669 Ok(new_wrapper_type)
670 })
671 }
672 Type::Reference(TypeReference {
673 elem,
674 mutability,
675 lifetime,
676 ..
677 }) => {
678 let reference_parameters = Some(ReferenceParameters {
679 is_mut: mutability.is_some(),
680 is_static: lifetime
681 .as_ref()
682 .map_or(false, |lifetime| lifetime.ident == "static"),
683 boxed: false,
684 });
685 if let Type::TraitObject(TypeTraitObject { bounds, .. }) = elem.as_ref() {
686 let new_wrapper_type =
687 self.translate_trait_wrapper_type(bounds, reference_parameters)?;
688 self.rust_types_wrappers.insert(new_wrapper_type.clone());
689 Ok(new_wrapper_type)
690 } else {
691 let wrapper = self.parse_and_register_rust_type(elem.as_ref());
692 wrapper.map(|wrapper| WrapperType {
693 reference_parameters,
694 ..wrapper
695 })
696 }
697 }
698 _ => Err(anyhow!("Unsupported type `{typ:?}`")),
699 }
700 }
701
702 fn parse_result_wrapper(
703 &mut self,
704 original_type: Type,
705 path_segment: &PathSegment,
706 ) -> anyhow::Result<WrapperType> {
707 ExternModuleTranslator::get_inner_generic_types(path_segment).and_then(|generic_types| {
708 let ok_type = self.parse_and_register_rust_type(
709 generic_types
710 .get(0)
711 .context("Result should have 2 generic types")?,
712 )?;
713 let exceptions_type = self.parse_rust_exceptions_type(
714 generic_types
715 .get(1)
716 .context("Result should have 2 generic types")?,
717 )?;
718 Ok(WrapperType {
719 original_type_name: original_type,
720 wrapper_name: format!(
721 "{}ResultWith{}",
722 &ok_type.wrapper_name, &exceptions_type.wrapper_name
723 ),
724 rust_type: RustWrapperType::Result(ok_type.boxed(), exceptions_type.boxed()),
725 reference_parameters: None,
726 impl_traits: vec![],
727 })
728 })
729 }
730
731 fn parse_option_wrapper(
732 &mut self,
733 original_type: Type,
734 path_segment: &PathSegment,
735 ) -> anyhow::Result<WrapperType> {
736 ExternModuleTranslator::get_inner_generic_type(path_segment)
737 .and_then(|inner_path| self.parse_and_register_rust_type(inner_path))
738 .map(|inner_type_name| WrapperType {
739 original_type_name: original_type,
740 wrapper_name: format!("Optional{}", &inner_type_name.wrapper_name),
741 rust_type: RustWrapperType::Option(inner_type_name.boxed()),
742 reference_parameters: None,
743 impl_traits: vec![],
744 })
745 }
746
747 fn parse_box_wrapper(&self, path_segment: &PathSegment) -> anyhow::Result<WrapperType> {
748 ExternModuleTranslator::get_inner_generic_type(path_segment).and_then(|inner_path| {
749 if let Type::TraitObject(TypeTraitObject { bounds, .. }) = inner_path {
750 self.translate_trait_wrapper_type(bounds, None)
751 } else {
752 Err(anyhow!("invalid Box inner type"))
753 }
754 })
755 }
756
757 fn parse_vec_wrapper(
758 &mut self,
759 original_type: Type,
760 path_segment: &PathSegment,
761 ) -> anyhow::Result<WrapperType> {
762 ExternModuleTranslator::get_inner_generic_type(path_segment)
763 .and_then(|inner_path| self.parse_and_register_rust_type(inner_path))
764 .map(|inner_type_name| WrapperType {
765 original_type_name: original_type,
766 wrapper_name: format!("Vec{}", &inner_type_name.wrapper_name),
767 rust_type: RustWrapperType::Vector(inner_type_name.boxed()),
768 reference_parameters: None,
769 impl_traits: vec![],
770 })
771 }
772
773 pub fn get_enum(&self, ident_str: &str) -> Option<&ItemEnum> {
774 self.shared_enums
775 .iter()
776 .find(|enum_item| enum_item.ident == ident_str)
777 }
778}
779
780fn parse_arc_wrapper(
781 original_type: Type,
782 path_segment: &PathSegment,
783) -> anyhow::Result<WrapperType> {
784 ExternModuleTranslator::get_inner_generic_type(path_segment).map(|inner_path| {
785 let generated_inner_type_name = inner_path
786 .to_token_stream()
787 .to_string()
788 .replace("dyn", "")
789 .replace(['<', '>', ' '], "");
790 WrapperType {
791 original_type_name: original_type,
792 wrapper_name: format!("Shared{}", &generated_inner_type_name),
793 rust_type: if generated_inner_type_name.starts_with("Mutex") {
794 RustWrapperType::ArcMutex
795 } else {
796 RustWrapperType::Arc
797 },
798 reference_parameters: None,
799 impl_traits: vec![],
800 }
801 })
802}
803
804fn parse_string_wrapper(original_type: Type) -> WrapperType {
805 WrapperType {
806 original_type_name: original_type,
807 wrapper_name: "RustString".to_string(),
808 rust_type: RustWrapperType::String,
809 reference_parameters: None,
810 impl_traits: vec![],
811 }
812}
813
814fn parse_primitive_wrapper(original_type: Type, primitive: &str) -> WrapperType {
815 WrapperType {
816 original_type_name: original_type,
817 wrapper_name: primitive.to_owned(),
818 rust_type: RustWrapperType::Primitive,
819 reference_parameters: None,
820 impl_traits: vec![],
821 }
822}
823
824fn parse_custom_wrapper(original_type: Type, path_segment: &PathSegment) -> WrapperType {
825 WrapperType {
826 original_type_name: original_type,
827 wrapper_name: path_segment.ident.to_string(),
828 rust_type: RustWrapperType::Custom,
829 reference_parameters: None,
830 impl_traits: vec![],
831 }
832}
833
834fn parse_enum_wrapper(enum_item: &ItemEnum) -> WrapperType {
835 let ident = &enum_item.ident;
836 WrapperType {
837 original_type_name: parse_quote! {#ident},
838 wrapper_name: ident.to_string(),
839 rust_type: if is_primitive_enum(enum_item) {
840 RustWrapperType::FieldlessEnum
841 } else {
842 RustWrapperType::DataEnum
843 },
844 reference_parameters: None,
845 impl_traits: vec![],
846 }
847}
848
849pub fn exception_wrapper_name(enum_ident: &str) -> String {
850 format!("{}Exceptions", &enum_ident)
851}