use quote::{quote, ToTokens};
use proc_macro2::TokenStream as TokenStream2;
use syn::{Attribute, GenericParam, Generics, Lifetime, LifetimeParam, ReturnType, Type};
use crate::ast::{CommaPunctuated, CommaPunctuatedTokens};
use crate::composer::{CommaPunctuatedArgs, TypePair};
use crate::presentation::{DictionaryExpr, DictionaryName, InterfacesMethodExpr};
#[allow(unused)]
#[derive(Clone, Debug)]
pub enum InterfacePresentation {
Empty,
Ctor {
attrs: Vec<Attribute>,
generics: Option<Generics>,
lifetimes: Vec<Lifetime>,
ffi_type: Type,
args: CommaPunctuatedTokens,
presentation: TokenStream2,
},
ConversionFrom {
attrs: Vec<Attribute>,
types: (
Type, Type ),
conversions: (
TokenStream2,
Option<Generics>,
Vec<Lifetime>
),
},
ConversionTo {
attrs: Vec<Attribute>,
types: (
Type, Type ),
conversions: (
TokenStream2,
Option<Generics>,
Vec<Lifetime>
),
},
Callback {
attrs: Vec<Attribute>,
lifetimes: Vec<Lifetime>,
ffi_type: Type,
inputs: CommaPunctuatedArgs,
output: ReturnType,
body: TokenStream2,
},
SendAndSync {
attrs: Vec<Attribute>,
ffi_type: Type,
},
Drop {
attrs: Vec<Attribute>,
ty: Type,
body: TokenStream2
}
}
impl InterfacePresentation {
pub fn conversion_from_root<T: ToTokens>(attrs: &[Attribute], types: &TypePair, body: T, generics: &Option<Generics>, lifetimes: &[Lifetime]) -> Self {
Self::conversion_from(attrs, types, DictionaryExpr::from_root(body), generics, lifetimes)
}
pub fn conversion_to_boxed<T: ToTokens + 'static>(attrs: &[Attribute], types: &TypePair, body: T, generics: &Option<Generics>, lifetimes: &[Lifetime]) -> Self {
Self::conversion_to(attrs, types, InterfacesMethodExpr::Boxed(body), generics, lifetimes)
}
pub fn conversion_to_boxed_self_destructured<T: ToTokens>(attrs: &[Attribute], types: &TypePair, body: T, generics: &Option<Generics>, lifetimes: &[Lifetime]) -> Self {
Self::conversion_to_boxed(attrs, types, DictionaryExpr::self_destruct(body), generics, lifetimes)
}
pub fn conversion_from<T: ToTokens>(attrs: &[Attribute], types: &TypePair, method_body: T, generics: &Option<Generics>, lifetimes: &[Lifetime]) -> Self {
InterfacePresentation::ConversionFrom {
attrs: attrs.to_owned(),
types: types.clone(),
conversions: (method_body.to_token_stream(), generics.clone(), lifetimes.to_owned())
}
}
pub fn non_generic_conversion_from<T: ToTokens>(attrs: &[Attribute], types: &TypePair, method_body: T, lifetimes: &[Lifetime]) -> Self {
InterfacePresentation::ConversionFrom {
attrs: attrs.to_owned(),
types: types.clone(),
conversions: (method_body.to_token_stream(), None, lifetimes.to_owned())
}
}
pub fn conversion_to<T: ToTokens>(attrs: &[Attribute], types: &TypePair, method_body: T, generics: &Option<Generics>, lifetimes: &[Lifetime]) -> Self {
InterfacePresentation::ConversionTo {
attrs: attrs.to_owned(),
types: types.clone(),
conversions: (method_body.to_token_stream(), generics.clone(), lifetimes.to_owned())
}
}
pub fn non_generic_conversion_to<T: ToTokens>(attrs: &[Attribute], types: &TypePair, method_body: T, lifetimes: &[Lifetime]) -> Self {
InterfacePresentation::ConversionTo {
attrs: attrs.to_owned(),
types: types.clone(),
conversions: (method_body.to_token_stream(), None, lifetimes.to_owned())
}
}
pub fn drop<T: ToTokens>(attrs: &[Attribute], ty: Type, body: T) -> Self {
InterfacePresentation::Drop { attrs: attrs.to_owned(), ty, body: body.to_token_stream() }
}
pub fn callback<T: ToTokens, U: ToTokens>(attrs: &[Attribute], lifetimes: &[Lifetime], ffi_type: Type, inputs: CommaPunctuatedArgs, output: ReturnType, args_conversions: T, result_conversion: U) -> Self {
InterfacePresentation::Callback {
attrs: attrs.to_owned(),
ffi_type,
inputs,
output,
lifetimes: lifetimes.to_owned(),
body: DictionaryExpr::callback_caller(args_conversions, result_conversion).to_token_stream(),
}
}
pub fn send_sync(attrs: &[Attribute], ffi_type: &Type) -> Self {
InterfacePresentation::SendAndSync { attrs: attrs.to_owned(), ffi_type: ffi_type.clone() }
}
}
fn generics_presentation(generics: &Option<Generics>, lifetimes: &[Lifetime]) -> (TokenStream2, TokenStream2) {
let result = match generics {
Some(generics) => {
let mut params = CommaPunctuated::from_iter(lifetimes.iter().map(|lt| GenericParam::Lifetime(LifetimeParam {
attrs: vec![],
lifetime: lt.clone(),
colon_token: None,
bounds: Default::default(),
})));
params.extend(generics.params.clone());
let gens = params.iter().map(ToTokens::to_token_stream);
let where_clause = match &generics.where_clause {
Some(where_clause) => {
let where_predicates = where_clause.predicates.iter().map(ToTokens::to_token_stream);
quote!(where <#(#where_predicates)*,>)
}
None => Default::default()
};
let generic_bounds = if gens.len() > 0 {
quote!(<#(#gens)*,>)
} else {
Default::default()
};
(generic_bounds, where_clause)
},
None => {
let lifetimes = CommaPunctuated::from_iter(lifetimes.iter().map(|lt| GenericParam::Lifetime(LifetimeParam {
attrs: vec![],
lifetime: lt.clone(),
colon_token: None,
bounds: Default::default(),
})));
let bounds = if lifetimes.is_empty() { Default::default() } else { quote!(<#lifetimes>) };
(bounds, Default::default())
}
};
result
}
impl ToTokens for InterfacePresentation {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
Self::Empty =>
quote!(),
Self::Ctor { attrs, ffi_type, generics, lifetimes, args, presentation } => {
let (generic_bounds, where_clause) = generics_presentation(generics, lifetimes);
quote! {
#(#attrs)*
impl #generic_bounds #ffi_type #where_clause {
pub fn new(#args) -> Self {
#presentation
}
}
}
},
Self::ConversionFrom {
attrs,
types: (ffi_type, target_type),
conversions: (presentation, generics, lifetimes),
} => {
let (generic_bounds, where_clause) = generics_presentation(generics, lifetimes);
let package = DictionaryName::Package;
let interface_from = DictionaryName::InterfaceFrom;
quote! {
#(#attrs)*
impl #generic_bounds #package::#interface_from<#target_type> for #ffi_type #where_clause {
unsafe fn ffi_from_const(ffi: *const #ffi_type) -> #target_type {
#presentation
}
}
}
},
Self::ConversionTo {
attrs,
types: (ffi_type, target_type),
conversions: (presentation, generics, lifetimes),
} => {
let (generic_bounds, where_clause) = generics_presentation(generics, lifetimes);
let package = DictionaryName::Package;
let interface_to = DictionaryName::InterfaceTo;
let obj = DictionaryName::Obj;
quote! {
#(#attrs)*
impl #generic_bounds #package::#interface_to<#target_type> for #ffi_type #where_clause {
unsafe fn ffi_to_const(#obj: #target_type) -> *const #ffi_type {
#presentation
}
}
}
},
Self::Callback { attrs, ffi_type, inputs, output, lifetimes, body } => {
let lifetimes = CommaPunctuated::from_iter(lifetimes.iter().map(|lt| GenericParam::Lifetime(LifetimeParam {
attrs: vec![],
lifetime: lt.clone(),
colon_token: None,
bounds: Default::default(),
})));
let bounds = if lifetimes.is_empty() {
Default::default()
} else {
quote!(<#lifetimes>)
};
quote! {
#(#attrs)*
impl #ffi_type {
pub unsafe fn call #bounds(&self, #inputs) #output {
#body
}
}
}
}
Self::SendAndSync { attrs, ffi_type } => {
quote! {
#(#attrs)*
unsafe impl Send for #ffi_type {}
#(#attrs)*
unsafe impl Sync for #ffi_type {}
}
},
Self::Drop { attrs, ty, body } => quote! {
#(#attrs)*
impl Drop for #ty { fn drop(&mut self) { unsafe { #body; } } }
}
}.to_tokens(tokens)
}
}