use std::marker::PhantomData;
use proc_macro2::{Ident, TokenStream};
use quote::{quote, ToTokens};
use syn::__private::TokenStream2;
use syn::{parse_quote, TraitBound, Type, TypeArray, TypeImplTrait, TypeParamBound, TypePath, TypePtr, TypeReference, TypeSlice, TypeTraitObject};
use crate::ast::AddPunctuated;
use crate::composable::{GenericBoundsModel, TraitModel, TypeModel};
use crate::context::ScopeContext;
use crate::kind::{DictFermentableModelKind, DictTypeModelKind, GroupModelKind, ObjectKind, SmartPointerModelKind, SpecialType, TypeModelKind};
use crate::ext::{Accessory, GenericNestedArg, Mangle, MaybeTraitBound, Resolve, ToType};
use crate::lang::objc::ObjCSpecification;
use crate::presentation::{FFIFullPath, FFIVariable};
impl ToType for FFIVariable<ObjCSpecification, TokenStream2> {
fn to_type(&self) -> Type {
match self {
FFIVariable::Direct { ty, .. } => ty.to_type(),
FFIVariable::ConstPtr { ty, .. } => ty.joined_const().to_type(),
FFIVariable::MutPtr { ty, .. } => ty.joined_mut().to_type(),
FFIVariable::Ref { ty, .. } => ty.joined_ref().to_type(),
FFIVariable::MutRef { ty, .. } => ty.joined_mut_ref().to_type(),
FFIVariable::Dyn { ty, .. } => ty.joined_dyn().to_type()
}
}
}
impl ToTokens for FFIVariable<ObjCSpecification, TokenStream2> {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
FFIVariable::Direct { ty, .. } => ty.to_tokens(tokens),
FFIVariable::ConstPtr { ty, .. } => quote!(const #ty *).to_tokens(tokens),
FFIVariable::MutPtr { ty, .. } => quote!(#ty *).to_tokens(tokens),
FFIVariable::Ref { ty, .. } => quote!(&#ty).to_tokens(tokens),
FFIVariable::MutRef { ty, .. } => quote!(&#ty *).to_tokens(tokens),
FFIVariable::Dyn { ty, .. } => quote!(#ty *).to_tokens(tokens),
}
}
}
impl Accessory for FFIVariable<ObjCSpecification, TokenStream2> {
fn joined_mut(&self) -> Self {
match self {
FFIVariable::Direct { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::ConstPtr { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::MutPtr { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::Ref { ty , .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::MutRef { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::Dyn { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
}
}
fn joined_const(&self) -> Self {
match self {
FFIVariable::Direct { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::ConstPtr { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::MutPtr { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::Ref { ty , .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::MutRef { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
FFIVariable::Dyn { ty, .. } => FFIVariable::MutPtr { ty: ty.clone(), _marker: PhantomData },
}
}
fn joined_dyn(&self) -> Self {
self.clone()
}
fn joined_ref(&self) -> Self {
todo!()
}
fn joined_mut_ref(&self) -> Self {
todo!()
}
fn joined_ident(&self, ident: &Ident) -> Self {
match self {
FFIVariable::Direct { ty, .. } => FFIVariable::Direct { ty: quote!(#ty::#ident), _marker: PhantomData },
FFIVariable::ConstPtr { ty, .. } => FFIVariable::ConstPtr { ty: quote!(#ty::#ident), _marker: PhantomData },
FFIVariable::MutPtr { ty, .. } => FFIVariable::MutPtr { ty: quote!(#ty::#ident), _marker: PhantomData },
FFIVariable::Ref { ty , .. } => FFIVariable::Ref { ty: quote!(#ty::#ident), _marker: PhantomData },
FFIVariable::MutRef { ty, .. } => FFIVariable::MutRef { ty: quote!(#ty::#ident), _marker: PhantomData },
FFIVariable::Dyn { ty, .. } => FFIVariable::Dyn { ty: quote!(#ty::#ident), _marker: PhantomData },
}
}
}
impl Resolve<FFIVariable<ObjCSpecification, TokenStream2>> for GenericBoundsModel {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<FFIVariable<ObjCSpecification, TokenStream2>> {
Some(self.resolve(source))
}
fn resolve(&self, _source: &ScopeContext) -> FFIVariable<ObjCSpecification, TokenStream2> {
let ffi_name = self.mangle_tokens_default();
if self.is_lambda() {
FFIVariable::direct(ffi_name)
} else {
FFIVariable::mut_ptr(ffi_name)
}
}
}
impl Resolve<FFIVariable<ObjCSpecification, TokenStream2>> for TraitBound {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<FFIVariable<ObjCSpecification, TokenStream2>> {
self.path.to_type().maybe_resolve(source)
}
fn resolve(&self, source: &ScopeContext) -> FFIVariable<ObjCSpecification, TokenStream2> {
self.path.to_type().resolve(source)
}
}
impl Resolve<FFIVariable<ObjCSpecification, TokenStream2>> for Type {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<FFIVariable<ObjCSpecification, TokenStream2>> {
Some(self.resolve(source))
}
fn resolve(&self, source: &ScopeContext) -> FFIVariable<ObjCSpecification, TokenStream2> {
let full_ty = Resolve::<Type>::resolve(self, source);
let maybe_special = Resolve::<SpecialType<ObjCSpecification>>::maybe_resolve(&full_ty, source);
let refined = maybe_special
.map(FFIFullPath::from)
.or_else(|| source.maybe_ffi_full_path(self))
.map(|ffi_path| ffi_path.to_type())
.unwrap_or_else(|| parse_quote!(#self));
resolve_type_variable(refined, source)
}
}
impl Resolve<FFIVariable<ObjCSpecification, TokenStream2>> for AddPunctuated<TypeParamBound> {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<FFIVariable<ObjCSpecification, TokenStream2>> {
Some(self.resolve(source))
}
fn resolve(&self, source: &ScopeContext) -> FFIVariable<ObjCSpecification, TokenStream2> {
self.iter().find_map(|bound| bound.maybe_trait_bound().map(|TraitBound { path, .. }| path.to_type())).unwrap().resolve(source)
}
}
pub fn resolve_type_variable(ty: Type, source: &ScopeContext) -> FFIVariable<ObjCSpecification, TokenStream2> {
match ty {
Type::Path(TypePath { path, .. }) =>
path.resolve(source),
Type::Array(TypeArray { elem, len, .. }) =>
FFIVariable::mut_ptr(parse_quote!([#elem; #len])),
Type::Reference(TypeReference { elem, .. }) |
Type::Slice(TypeSlice { elem, .. }) =>
elem.resolve(source),
Type::Ptr(TypePtr { const_token, mutability, elem, .. }) => match *elem {
Type::Path(TypePath { path, .. }) => {
let ty = if path.segments.last().unwrap().ident.eq("c_void") {
quote!(void)
} else {
path.to_token_stream()
};
if const_token.is_some() {
FFIVariable::const_ptr(ty)
} else {
FFIVariable::mut_ptr(ty)
}
},
Type::Ptr(..) =>
FFIVariable::mut_ptr(elem.to_token_stream()),
ty if mutability.is_some() =>
FFIVariable::mut_ptr(ty.to_token_stream()),
ty =>
FFIVariable::const_ptr(ty.to_token_stream())
},
Type::TraitObject(TypeTraitObject { dyn_token: _, bounds, .. }) => {
bounds.resolve(source)
}
Type::ImplTrait(TypeImplTrait { impl_token: _, bounds, .. }) =>
bounds.resolve(source),
ty =>
FFIVariable::direct(ty.mangle_tokens_default())
}
}
impl Resolve<FFIVariable<ObjCSpecification, TokenStream2>> for TypeModelKind {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<FFIVariable<ObjCSpecification, TokenStream2>> {
Some(self.resolve(source))
}
fn resolve(&self, source: &ScopeContext) -> FFIVariable<ObjCSpecification, TokenStream2> {
let result = match self {
TypeModelKind::FnPointer(TypeModel { ty, .. }, ..) =>
FFIVariable::direct(
Resolve::<SpecialType<ObjCSpecification>>::maybe_resolve(ty, source)
.map(|special| special.to_token_stream())
.unwrap_or_else(|| Resolve::<FFIFullPath<ObjCSpecification>>::resolve(ty, source)
.to_token_stream())),
TypeModelKind::Dictionary(DictTypeModelKind::LambdaFn(TypeModel { ty, .. }, ..)) =>
FFIVariable::mut_ptr(Resolve::<FFIFullPath<ObjCSpecification>>::resolve(ty, source).to_token_stream()),
TypeModelKind::Dictionary(DictTypeModelKind::Primitive(composition)) =>
FFIVariable::direct(composition.to_type().to_token_stream()),
TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::SmartPointer(SmartPointerModelKind::Box(TypeModel { ty, .. })))) => {
match ty.maybe_first_nested_type_ref() {
Some(nested_full_ty) => {
resolve_type_variable(match Resolve::<SpecialType<ObjCSpecification>>::maybe_resolve(nested_full_ty, source) {
Some(special) => special.to_type(),
None => {
source.maybe_ffi_full_path::<ObjCSpecification>(nested_full_ty)
.map(|full_path| full_path.to_type())
.unwrap_or_else(|| nested_full_ty.clone())
}
}, source)
}
None => panic!("error: Arg kind ({}) not supported", ty.to_token_stream())
}
},
TypeModelKind::Dictionary(
DictTypeModelKind::NonPrimitiveFermentable(
DictFermentableModelKind::SmartPointer(
SmartPointerModelKind::Arc(TypeModel { ty, .. }) |
SmartPointerModelKind::Mutex(TypeModel { ty, .. }) |
SmartPointerModelKind::Rc(TypeModel { ty, .. }) |
SmartPointerModelKind::Cell(TypeModel { ty, .. }) |
SmartPointerModelKind::RefCell(TypeModel { ty, .. }) |
SmartPointerModelKind::UnsafeCell(TypeModel { ty, .. }) |
SmartPointerModelKind::OnceLock(TypeModel { ty, .. }) |
SmartPointerModelKind::RwLock(TypeModel { ty, .. }) |
SmartPointerModelKind::Pin(TypeModel { ty, .. })
) |
DictFermentableModelKind::Group(
GroupModelKind::BTreeSet(TypeModel { ty, .. }) |
GroupModelKind::HashSet(TypeModel { ty, .. }) |
GroupModelKind::Map(TypeModel { ty, .. }) |
GroupModelKind::Result(TypeModel { ty, .. }) |
GroupModelKind::Vec(TypeModel { ty, .. }) |
GroupModelKind::IndexMap(TypeModel { ty, .. }) |
GroupModelKind::IndexSet(TypeModel { ty, .. })
) |
DictFermentableModelKind::Str(TypeModel { ty, .. }) |
DictFermentableModelKind::String(TypeModel { ty, .. }) |
DictFermentableModelKind::I128(TypeModel { ty, .. }) |
DictFermentableModelKind::U128(TypeModel { ty, .. }) |
DictFermentableModelKind::Other(TypeModel { ty, .. })
) |
DictTypeModelKind::NonPrimitiveOpaque(TypeModel { ty, .. })
) |
TypeModelKind::Trait(TraitModel { ty: TypeModel { ty, .. }, .. }, ..) |
TypeModelKind::TraitType(TypeModel { ty, .. }) |
TypeModelKind::Object(TypeModel { ty, .. }) |
TypeModelKind::Optional(TypeModel { ty, .. }) |
TypeModelKind::Array(TypeModel { ty, .. }) |
TypeModelKind::Slice(TypeModel { ty, .. }) |
TypeModelKind::Tuple(TypeModel { ty, .. }) |
TypeModelKind::Unknown(TypeModel { ty, .. }) => {
Resolve::<SpecialType<ObjCSpecification>>::maybe_resolve(ty, source)
.map(|ty| resolve_type_variable(FFIFullPath::from(ty).to_type(), source))
.unwrap_or_else(|| {
resolve_type_variable(Resolve::<ObjectKind>::maybe_resolve(ty, source)
.and_then(|external_type| external_type.maybe_fn_or_trait_or_same_kind(source))
.unwrap_or_else(|| TypeModelKind::unknown_type_ref(ty))
.to_type(), source)
})
},
TypeModelKind::Fn(TypeModel { ty, .. }, ..) => {
panic!("error: Arg kind (Fn) ({}) not supported", ty.to_token_stream())
},
TypeModelKind::Bounds(bounds) => {
bounds.resolve(source)
},
ty =>
panic!("error: Arg kind ({}) not supported", ty),
};
result
}
}