use std::collections::{HashMap, HashSet, VecDeque};
use roxygen::roxygen;
use crate::{
codegen::replace_types::{
convert_to_stub, generate_standard_allowed_prefixes,
generate_type_transmute_pair_assertions, replace_types_in_item, ParseConfig,
TypeTransmutePair,
},
RustEdition, SourceLocation,
};
pub struct Builder {
pub(crate) source_crate_name: String,
pub(crate) allowed_prefixes: Vec<syn::Path>,
pub(crate) prefixed_exported_types: Vec<syn::Path>,
pub(crate) transparent_wrappers: Vec<syn::Path>,
pub(crate) edition: RustEdition,
}
impl Builder {
pub fn new(source_crate_name: impl Into<String>) -> Self {
Self {
source_crate_name: source_crate_name.into(),
allowed_prefixes: generate_standard_allowed_prefixes(),
prefixed_exported_types: Vec::new(),
transparent_wrappers: Vec::new(),
edition: RustEdition::default(),
}
}
#[roxygen]
pub fn allowed_prefix<S: AsRef<str>>(
mut self,
prefix: S,
) -> Self {
let path: syn::Path = syn::parse_str(prefix.as_ref()).unwrap();
self.allowed_prefixes.push(path);
self
}
#[roxygen]
pub fn strip_transparent_wrapper<S: AsRef<str>>(
mut self,
wrapper: S,
) -> Self {
let path: syn::Path = syn::parse_str(wrapper.as_ref()).unwrap();
self.transparent_wrappers.push(path);
self
}
#[roxygen]
pub fn prefixed_exported_type<S: AsRef<str>>(
mut self,
full_type_path: S,
) -> Self {
let path: syn::Path = syn::parse_str(full_type_path.as_ref()).unwrap();
self.prefixed_exported_types.push(path);
self
}
#[roxygen]
pub fn edition(
mut self,
edition: RustEdition,
) -> Self {
self.edition = edition;
self
}
pub fn build(self) -> FfiConverter {
let mut primitive_types = HashMap::new();
for primitive in [
"bool", "char", "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64",
"u128", "usize", "f32", "f64", "str",
] {
primitive_types.insert(primitive.to_string(), primitive.to_string());
}
FfiConverter {
builder: self,
stage: GenerationStage::Collect,
source_items: VecDeque::new(),
type_replacements: HashMap::new(),
exported_types: HashSet::new(),
primitive_types,
followup_items: Vec::new(),
}
}
}
enum GenerationStage {
Collect,
Convert,
Followup,
}
pub struct FfiConverter {
pub(crate) builder: Builder,
stage: GenerationStage,
source_items: VecDeque<(syn::Item, SourceLocation)>,
exported_types: HashSet<String>,
primitive_types: HashMap<String, String>,
type_replacements: HashMap<TypeTransmutePair, SourceLocation>,
followup_items: Vec<(syn::Item, SourceLocation)>,
}
impl FfiConverter {
pub fn builder(source_crate_name: impl Into<String>) -> Builder {
Builder::new(source_crate_name)
}
fn collect_item(&mut self, item: syn::Item, source_location: SourceLocation) {
match &item {
syn::Item::Struct(s) => {
let type_name = s.ident.to_string();
self.exported_types.insert(type_name.clone());
}
syn::Item::Enum(e) => {
let type_name = e.ident.to_string();
self.exported_types.insert(type_name.clone());
}
syn::Item::Union(u) => {
let type_name = u.ident.to_string();
self.exported_types.insert(type_name.clone());
}
syn::Item::Type(t) => {
let type_name = t.ident.to_string();
self.exported_types.insert(type_name.clone());
if let syn::Type::Path(type_path) = &*t.ty {
if let Some(last_segment) = type_path.path.segments.last() {
let target_type = last_segment.ident.to_string();
if let Some(basic_type) = self.primitive_types.get(&target_type).cloned() {
self.primitive_types
.insert(type_name.clone(), basic_type.clone());
let prefixed_path: syn::Path = syn::parse_str(&format!(
"{}::{}",
self.builder.source_crate_name.replace('-', "_"),
type_name
))
.unwrap();
let prefixed_name = quote::quote! { #prefixed_path }.to_string();
self.primitive_types.insert(prefixed_name, basic_type);
}
}
}
}
_ => {}
}
self.source_items.push_back((item, source_location));
}
fn convert(&mut self) -> Option<(syn::Item, SourceLocation)> {
if let Some((mut item, source_location)) = self.source_items.pop_front() {
let config = ParseConfig {
crate_name: &self.builder.source_crate_name,
exported_types: &self.exported_types,
primitive_types: &self.primitive_types,
allowed_prefixes: &self.builder.allowed_prefixes,
prefixed_exported_types: &self.builder.prefixed_exported_types,
transparent_wrappers: &self.builder.transparent_wrappers,
edition: self.builder.edition,
};
match item {
syn::Item::Fn(ref mut function) => convert_to_stub(
function,
&config,
&mut self.type_replacements,
&source_location,
),
_ => replace_types_in_item(
&mut item,
&config,
&mut self.type_replacements,
&source_location,
),
}
return Some((item, source_location));
}
None
}
fn generate_assertions(&mut self) {
for (replacement, source_location) in &self.type_replacements {
if let Some((size_assertion, align_assertion)) =
generate_type_transmute_pair_assertions(replacement)
{
self.followup_items
.push((size_assertion, source_location.clone()));
self.followup_items
.push((align_assertion, source_location.clone()));
}
}
}
pub fn call<I>(&mut self, iter: &mut I) -> Option<(syn::Item, SourceLocation)>
where
I: Iterator<Item = (syn::Item, SourceLocation)>,
{
loop {
match self.stage {
GenerationStage::Collect => {
if let Some((item, source_location)) = iter.next() {
self.collect_item(item, source_location);
} else {
self.stage = GenerationStage::Convert;
}
}
GenerationStage::Convert => {
if let Some((item, source_location)) = self.convert() {
return Some((item, source_location));
} else {
self.generate_assertions();
self.stage = GenerationStage::Followup;
}
}
GenerationStage::Followup => {
if let Some((item, source_location)) = self.followup_items.pop() {
return Some((item, source_location));
} else {
return None; }
}
}
}
}
pub fn into_closure<I>(mut self) -> impl FnMut(&mut I) -> Option<(syn::Item, SourceLocation)>
where
I: Iterator<Item = (syn::Item, SourceLocation)>,
{
move |iter| self.call(iter)
}
}