ferment-sys 0.2.14

Syntax tree morphing of FFI-compatible stuff
Documentation
use std::fmt::Debug;
use quote::quote;
use syn::{parse_quote, Type, TypeReference};
use crate::composable::TypeModel;
use crate::composer::SourceComposable;
use crate::context::{ScopeContext, ScopeSearch, ScopeSearchKey};
use crate::kind::{DictFermentableModelKind, DictTypeModelKind, GenericTypeKind, ObjectKind, SmartPointerModelKind, TypeKind, TypeModelKind};
use crate::ext::{ExpressionComposable, FFISpecialTypeResolve, GenericNestedArg, Primitive, Resolve, ToType};
use crate::lang::Specification;
use crate::presentable::{ConversionExpressionKind, Expression, ScopeContextPresentable};
use crate::presentation::{FFIFullDictionaryPath, FFIFullPath};


#[derive(Clone, Debug)]
pub struct ConversionDropComposer<SPEC>
where SPEC: Specification {
    pub name: SPEC::Name,
    pub search: ScopeSearch,
    pub expr: Option<SPEC::Expr>,
}
impl<SPEC> ConversionDropComposer<SPEC>
where SPEC: Specification {
    fn new(name: SPEC::Name, search: ScopeSearch, expr: Option<SPEC::Expr>) -> Self {
        Self { name, search, expr }
    }
    #[allow(unused)]
    pub fn key_expr_in_composer_scope(name: SPEC::Name, ty: &Type, expr: Option<SPEC::Expr>) -> Self {
        Self::new(name, ScopeSearch::type_ref_key_in_composer_scope(ty), expr)
    }
    #[allow(unused)]
    pub fn key_ref_expr_in_composer_scope(name: &SPEC::Name, ty: &Type, expr: Option<SPEC::Expr>) -> Self {
        Self::key_expr_in_composer_scope(name.clone(), ty, expr)
    }
    fn value_expr_in_composer_scope(name: SPEC::Name, ty: &Type, expr: Option<SPEC::Expr>) -> Self {
        Self::new(name, ScopeSearch::type_ref_value(ty), expr)
    }

    #[allow(unused)]
    pub fn value(name: SPEC::Name, ty: &Type) -> Self {
        Self::value_expr_in_composer_scope(name, ty, None)
    }
    #[allow(unused)]
    pub fn value_ref(name: &SPEC::Name, ty: &Type) -> Self {
        Self::value(name.clone(), ty)
    }
    #[allow(unused)]
    pub fn value_expr(name: SPEC::Name, ty: &Type, expr: SPEC::Expr) -> Self {
        Self::value_expr_in_composer_scope(name, ty, Some(expr))
    }

    #[allow(unused)]
    pub fn value_ref_expr(name: &SPEC::Name, ty: &Type, expr: SPEC::Expr) -> Self {
        Self::value_expr(name.clone(), ty, expr)
    }

}
impl<SPEC> SourceComposable for ConversionDropComposer<SPEC>
where SPEC: Specification<Expr=Expression<SPEC>>,
      SPEC::Expr: ScopeContextPresentable,
      FFIFullPath<SPEC>: ToType,
      FFIFullDictionaryPath<SPEC>: ToType {
    type Source = ScopeContext;
    type Output = Option<SPEC::Expr>;

    fn compose(&self, source: &Self::Source) -> Self::Output {
        let Self { name, search, expr, .. } = self;
        let search_key = self.search.search_key();
        let field_path = expr.clone().unwrap_or_else(|| SPEC::Expr::simple(name));
        let maybe_object = source.maybe_object_by_predicate_ref(search);
        let full_type = maybe_object
            .as_ref()
            .and_then(ObjectKind::maybe_type)
            .unwrap_or_else(|| search_key.to_type());
        let full_type = match &full_type {
            Type::Reference(TypeReference { elem, .. }) => *elem.clone(),
            _ => full_type
        };
        let ffi_type = Resolve::<FFIFullPath<SPEC>>::resolve(&full_type, source).to_type();
        match FFISpecialTypeResolve::<SPEC>::maybe_special_type(&full_type, source) {
            Some(special) =>
                Some(SPEC::Expr::cast_destroy(field_path, ConversionExpressionKind::Complex, special, full_type)),
            _ => match maybe_object
                .and_then(|kind| kind.maybe_trait_or_same_kind(source))
                .unwrap_or_else(|| TypeModelKind::unknown_type(search_key.to_type())) {
                TypeModelKind::Dictionary(DictTypeModelKind::LambdaFn(..)) =>
                    None,
                TypeModelKind::FnPointer(..) =>
                    source.maybe_lambda_args::<SPEC>(&full_type)
                        .map(|_| SPEC::Expr::cast_destroy(field_path, ConversionExpressionKind::Complex, ffi_type, full_type)),
                TypeModelKind::Optional(..) =>
                    full_type.maybe_first_nested_type()
                        .map(|target_ty| SPEC::Expr::cast_destroy(field_path, if full_type.is_primitive() { ConversionExpressionKind::PrimitiveOpt } else { ConversionExpressionKind::ComplexOpt }, ffi_type, target_ty)),
                TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::Str(TypeModel { ty: ref full_ty, .. }))) =>
                    Some(SPEC::Expr::destroy_string(field_path, quote!(&#full_ty))),
                TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::String(TypeModel { ty: ref full_ty, .. }))) =>
                    Some(SPEC::Expr::destroy_string(field_path, full_ty)),
                TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::I128(..))) =>
                    Some(SPEC::Expr::destroy_big_int(field_path, quote!([u8; 16]), quote!(i128))),
                TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::U128(..))) =>
                    Some(SPEC::Expr::destroy_big_int(field_path, quote!([u8; 16]), quote!(u128))),
                TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::SmartPointer(SmartPointerModelKind::Box(TypeModel { ty: ref full_ty, .. })))) =>
                    full_ty.maybe_first_nested_type()
                        .map(|first_nested_ty| SPEC::Expr::cast_destroy(field_path, ConversionExpressionKind::Complex, ffi_type, first_nested_ty)),
                TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::Cow(TypeModel { ty: ref full_ty, .. }))) =>
                    full_ty.maybe_first_nested_type_ref()
                        .and_then(|first_nested_type_ref| (!first_nested_type_ref.is_primitive())
                            .then(|| SPEC::Expr::cast_destroy(field_path, ConversionExpressionKind::Complex, ffi_type, first_nested_type_ref.clone()))),
                TypeModelKind::Bounds(..) =>
                    Some(SPEC::Expr::destroy_complex(field_path)),
                TypeModelKind::Slice(TypeModel { ref ty, .. }) =>
                    ty.maybe_first_nested_type_ref()
                        .and_then(|first_nested_ty| destroy_other::<SPEC>(search_key, ffi_type, parse_quote!(Vec<#first_nested_ty>), field_path)),
                _ =>
                    destroy_other::<SPEC>(search_key, ffi_type, full_type, field_path)
            }
        }
    }
}


fn destroy_other<SPEC>(ty: &ScopeSearchKey, ffi_type: Type, target_ty: Type, field_path: SPEC::Expr) -> Option<SPEC::Expr>
where SPEC: Specification<Expr=Expression<SPEC>>,
      SPEC::Expr: ScopeContextPresentable {
    match TypeKind::from(ty.to_type()) {
        TypeKind::Primitive(_) =>
            None,
        TypeKind::Generic(GenericTypeKind::Optional(ty) | GenericTypeKind::Cow(ty)) => Some(match ty.maybe_first_nested_type_kind() {
            Some(TypeKind::Primitive(_)) => ConversionExpressionKind::PrimitiveOpt,
            _ => ConversionExpressionKind::ComplexOpt,
        }),
        _ =>
            Some(ConversionExpressionKind::Complex)
    }.map(|kind| Expression::cast_destroy(field_path, kind, ffi_type, target_ty))
}