mod ffi_resolver;
mod maybe_lambda_args;
mod opaque;
mod resolve_attrs;
mod resolve_trait;
use proc_macro2::Ident;
use quote::ToTokens;
use syn::{Path, TraitBound, Type, TypePath, TypeReference, TypeTraitObject};
use crate::composable::TraitModel;
use crate::context::{ScopeContext, ScopeSearchKey};
use crate::ext::{AsType, CrateExtension, DictionaryType, Join, Mangle, MaybeAngleBracketedArgs, MaybeGenericType, MaybeTraitBound, ToPath, ToType, CRATE};
use crate::kind::{GenericTypeKind, ObjectKind, SpecialType, TypeModelKind};
use crate::lang::Specification;
use crate::presentation::{FFIFullDictionaryPath, FFIFullPath};
pub use self::ffi_resolver::*;
pub use self::maybe_lambda_args::*;
pub use self::opaque::*;
pub use self::resolve_attrs::*;
pub use self::resolve_trait::*;
pub trait Resolve<T> {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<T>;
fn resolve(&self, source: &ScopeContext) -> T;
}
impl Resolve<Type> for Type {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<Type> {
Some(self.resolve(source))
}
fn resolve(&self, source: &ScopeContext) -> Type {
source.full_type_for(self)
}
}
impl Resolve<ObjectKind> for Type {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<ObjectKind> {
source.maybe_object_by_key(self)
}
fn resolve(&self, source: &ScopeContext) -> ObjectKind {
self.maybe_resolve(source)
.unwrap_or_else(|| panic!("Can't resolve ObjectKind for Type({})", self.to_token_stream()))
}
}
impl<SPEC> Resolve<SpecialType<SPEC>> for Type
where SPEC: Specification,
FFIFullDictionaryPath<SPEC>: ToType {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<SpecialType<SPEC>> {
source.maybe_custom_conversion(self)
.map(SpecialType::Custom)
.or_else(|| source.maybe_opaque_object::<SPEC>(self)
.map(SpecialType::Opaque))
}
fn resolve(&self, source: &ScopeContext) -> SpecialType<SPEC> {
self.maybe_resolve(source)
.unwrap_or_else(|| panic!("Can't resolve SpecialType for Type({})", self.to_token_stream()))
}
}
impl<SPEC> Resolve<SpecialType<SPEC>> for ScopeSearchKey
where SPEC: Specification,
FFIFullDictionaryPath<SPEC>: ToType {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<SpecialType<SPEC>> {
let ty = self.to_type();
source.maybe_custom_conversion(&ty)
.map(SpecialType::Custom)
.or_else(|| source.maybe_opaque_object::<SPEC>(&ty)
.map(SpecialType::Opaque))
}
fn resolve(&self, source: &ScopeContext) -> SpecialType<SPEC> {
self.maybe_resolve(source)
.unwrap_or_else(|| panic!("Can't resolve SpecialType for ScopeSearchKey({})", self.to_token_stream()))
}
}
impl Resolve<TypeModelKind> for ScopeSearchKey {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<TypeModelKind> {
Some(self.resolve(source))
}
fn resolve(&self, source: &ScopeContext) -> TypeModelKind {
self.to_type().resolve(source)
}
}
impl Resolve<TypeModelKind> for Type {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<TypeModelKind> {
Some(self.resolve(source))
}
fn resolve(&self, source: &ScopeContext) -> TypeModelKind {
Resolve::<ObjectKind>::maybe_resolve(self, source)
.and_then(|ext_obj_kind| ext_obj_kind.maybe_fn_or_trait_or_same_kind(source))
.unwrap_or_else(|| TypeModelKind::unknown_type_ref(self))
}
}
impl<SPEC> Resolve<FFIFullPath<SPEC>> for Type
where SPEC: Specification {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<FFIFullPath<SPEC>> {
match self {
Type::Path(TypePath { path, .. }) =>
path.maybe_resolve(source),
Type::Reference(TypeReference { elem, .. }) =>
elem.maybe_resolve(source),
Type::Array(..) |
Type::Slice(..) |
Type::Tuple(..) =>
Some(FFIFullPath::generic(self.mangle_ident_default().to_path())),
Type::TraitObject(TypeTraitObject { bounds, .. }) => match bounds.len() {
0 => unimplemented!("TODO: FFIResolver::resolve::Type::TraitObject (Empty)"),
1 => bounds.first()
.and_then(MaybeTraitBound::maybe_trait_bound)
.and_then(|TraitBound { path, .. }| path.maybe_resolve(source)),
_ => Some(FFIFullPath::generic(bounds.mangle_ident_default().to_path())),
},
_ => None
}
}
fn resolve(&self, source: &ScopeContext) -> FFIFullPath<SPEC> {
Resolve::<FFIFullPath<SPEC>>::maybe_resolve(self, source)
.unwrap_or_else(|| FFIFullPath::external(self.to_path()))
}
}
impl<SPEC> Resolve<SpecialType<SPEC>> for GenericTypeKind
where SPEC: Specification,
FFIFullDictionaryPath<SPEC>: ToType {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<SpecialType<SPEC>> {
self.ty()
.and_then(|ty| ty.maybe_resolve(source))
}
fn resolve(&self, source: &ScopeContext) -> SpecialType<SPEC> {
self.maybe_resolve(source)
.unwrap_or_else(|| panic!("Can't resolve SpecialType for GenericTypeKind({})", self.to_token_stream()))
}
}
impl<SPEC> Resolve<SpecialType<SPEC>> for TypeModelKind
where SPEC: Specification,
FFIFullDictionaryPath<SPEC>: ToType {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<SpecialType<SPEC>> {
self.as_type().maybe_resolve(source)
}
fn resolve(&self, source: &ScopeContext) -> SpecialType<SPEC> {
self.maybe_resolve(source)
.unwrap_or_else(|| panic!("Can't resolve SpecialType for TypeModelKind({})", self.to_token_stream()))
}
}
impl<SPEC> Resolve<FFIFullPath<SPEC>> for TypeModelKind
where SPEC: Specification,
FFIFullDictionaryPath<SPEC>: ToType {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<FFIFullPath<SPEC>> {
self.as_type().maybe_resolve(source)
}
fn resolve(&self, source: &ScopeContext) -> FFIFullPath<SPEC> {
self.maybe_resolve(source)
.unwrap_or_else(|| panic!("Can't resolve SpecialType for TypeModelKind({})", self.to_token_stream()))
}
}
impl Resolve<Type> for Ident {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<Type> {
Resolve::<Type>::maybe_resolve(&self.to_type(), source)
}
fn resolve(&self, source: &ScopeContext) -> Type {
Resolve::<Type>::resolve(&self.to_type(), source)
}
}
impl Resolve<Type> for Path {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<Type> {
Resolve::<Type>::maybe_resolve(&self.to_type(), source)
}
fn resolve(&self, source: &ScopeContext) -> Type {
Resolve::<Type>::resolve(&self.to_type(), source)
}
}
impl<SPEC> Resolve<FFIFullPath<SPEC>> for Path
where SPEC: Specification {
fn maybe_resolve(&self, source: &ScopeContext) -> Option<FFIFullPath<SPEC>> {
let segments = &self.segments;
let first_segment = segments.first()?;
let last_segment = segments.last()?;
let first_ident = &first_segment.ident;
let last_ident = &last_segment.ident;
if last_ident.is_primitive() {
None
} else if last_ident.is_any_string() {
Some(FFIFullPath::c_char())
} else if last_ident.is_special_generic() ||
(last_ident.is_result() && segments.len() == 1) ||
last_ident.eq("Map") && first_ident.eq("serde_json") || last_ident.is_lambda_fn() {
Some(FFIFullPath::generic(self.mangle_ident_default().to_path()))
} else if last_ident.is_optional() || last_ident.is_box() || last_ident.is_cow() {
last_segment.maybe_angle_bracketed_args()
.and_then(MaybeGenericType::maybe_generic_type)
.and_then(|ty| ty.maybe_resolve(source))
} else if last_ident.is_smart_ptr() {
Some(FFIFullPath::generic(self.mangle_ident_default().to_path()))
} else {
let chunk = if let Some(
ObjectKind::Type(TypeModelKind::Trait(TraitModel { ty: model, .. })) |
ObjectKind::Type(TypeModelKind::TraitType(model))
) = self.maybe_trait_object(source) {
&model.as_type().to_path().segments
} else {
segments
};
maybe_crate_ident_replacement(&chunk.first()?.ident, source)
.map(|crate_ident| {
let crate_local_segments = chunk.crate_and_ident_less();
FFIFullPath::r#type(crate_ident.clone(), if crate_local_segments.is_empty() {
crate_ident.to_path().joined(last_ident).mangle_ident_default().to_path()
} else {
crate_local_segments.joined(&chunk.ident_less().joined(last_ident).mangle_ident_default()).to_path()
})
})
.or_else(|| {
let segments = chunk.ident_less();
Some(FFIFullPath::external(if segments.is_empty() {
last_ident.to_path()
} else {
segments.joined(last_ident).to_path()
}))
})
}
}
fn resolve(&self, source: &ScopeContext) -> FFIFullPath<SPEC> {
self.maybe_resolve(source)
.unwrap_or_else(|| panic!("Can't resolve FFIFullPath for Path({})", self.to_token_stream()))
}
}
fn maybe_crate_ident_replacement<'a>(ident: &'a Ident, source: &'a ScopeContext) -> Option<&'a Ident> {
let lock = source.context.borrow();
match ident.to_string().as_str() {
CRATE =>
Some(source.scope.crate_ident_ref()),
_ if lock.config.is_current_crate(ident) =>
Some(source.scope.crate_ident_ref()),
_ if lock.config.contains_fermented_crate(ident) =>
Some(ident),
_ =>
None
}
}