ferment-sys 0.2.14

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

#[derive(Clone, Debug)]
pub struct ConversionToComposer<SPEC>
where SPEC: Specification {
    pub name: SPEC::Name,
    pub search: ScopeSearch,
    pub expr: Option<SPEC::Expr>,
}

impl<SPEC> ConversionToComposer<SPEC>
where SPEC: Specification {
    pub fn new(name: SPEC::Name, search: ScopeSearch, expr: Option<SPEC::Expr>) -> Self {
        Self { name, search, expr }
    }
    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)
    }
    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)
    }
    pub fn value_maybe_expr(name: SPEC::Name, ty: &Type, expr: Option<SPEC::Expr>) -> Self {
        Self::new(name, ScopeSearch::type_ref_value(ty), expr)
    }
    pub fn value_ref_maybe_expr(name: &SPEC::Name, ty: &Type, expr: Option<SPEC::Expr>) -> Self {
        Self::value_maybe_expr(name.clone(), ty, expr)
    }
    pub fn key_in_composer_scope(name: SPEC::Name, ty: &Type) -> Self {
        Self::key_expr_in_composer_scope(name, ty, None)
    }
    pub fn value(name: SPEC::Name, ty: &Type) -> Self {
        Self::value_maybe_expr(name, ty, None)
    }
    pub fn value_ref(name: &SPEC::Name, ty: &Type) -> Self {
        Self::value(name.clone(), ty)
    }
    pub fn value_expr(name: SPEC::Name, ty: &Type, expr: SPEC::Expr) -> Self {
        Self::value_maybe_expr(name, ty, Some(expr))
    }
    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 ConversionToComposer<SPEC>
where SPEC: Specification<Expr=Expression<SPEC>>,
      SPEC::Expr: ScopeContextPresentable,
      FFIFullPath<SPEC>: ToType,
      FFIFullDictionaryPath<SPEC>: ToType {
    type Source = ScopeContext;
    type Output = 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 is_ref = search_key.maybe_originally_is_ref();
        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();
        let result = match maybe_object {
            Some(ObjectKind::Item(.., ScopeItemKind::Fn(..))) => match &source.scope.parent_object() {
                Some(ObjectKind::Type(ref ty_conversion) | ObjectKind::Item(ref ty_conversion, ..)) => {
                    let full_parent_ty: Type = Resolve::resolve(ty_conversion.as_type(), source);
                    match Resolve::<SpecialType<SPEC>>::maybe_resolve(&full_parent_ty, source) {
                        Some(SpecialType::Opaque(..)) if is_ref =>
                            Expression::boxed_tokens(DictionaryExpr::Clone(name.to_token_stream())),
                        Some(SpecialType::Opaque(..)) =>
                            Expression::boxed_tokens(name),
                        Some(SpecialType::Custom(custom_ty)) =>
                            Expression::cast_to(field_path, ConversionExpressionKind::Complex, custom_ty, full_parent_ty),
                        _ =>
                            Expression::cast_to(field_path, ConversionExpressionKind::Complex, ffi_type, full_parent_ty),
                    }
                },
                _ => Expression::cast_to(if is_ref { field_path.cloned() } else { field_path }, ConversionExpressionKind::from(&full_type), ffi_type, full_type)
            },
            Some(ObjectKind::Item(ty_kind, ..) |
                 ObjectKind::Type(ty_kind)) => match Resolve::<SpecialType<SPEC>>::maybe_resolve(&full_type, source) {
                Some(SpecialType::Opaque(..)) if search_key.maybe_originally_is_ptr() =>
                    field_path,
                Some(SpecialType::Opaque(..)) if is_ref =>
                    Expression::boxed(field_path.cloned()),
                Some(SpecialType::Opaque(..)) =>
                    Expression::boxed(field_path),
                Some(SpecialType::Custom(custom_ty)) =>
                    Expression::cast_to(field_path, ConversionExpressionKind::Complex, custom_ty, full_type),
                _ => match ty_kind {
                    TypeModelKind::FnPointer(..) |
                    TypeModelKind::Dictionary(DictTypeModelKind::LambdaFn(..)) =>
                        Expression::cast_to(field_path, ConversionExpressionKind::Complex, ffi_type, full_type),
                    TypeModelKind::Optional(TypeModel { ty, .. }) => match ty.maybe_first_nested_type_kind() {
                        None =>
                            Expression::cast_to(if is_ref { field_path.cloned() } else { field_path }, ConversionExpressionKind::from(&full_type), ffi_type, full_type),
                        Some(nested_ty_kind) => {
                            let nested_ty = nested_ty_kind.to_type();
                            let (expr_kind, ffi_type) = match FFISpecialTypeResolve::<SPEC>::maybe_special_type(&nested_ty, source) {
                                Some(SpecialType::Custom(custom_ty)) => (ConversionExpressionKind::ComplexOpt, custom_ty),
                                Some(SpecialType::Opaque(opaque_ty)) => (ConversionExpressionKind::PrimitiveOpt, opaque_ty),
                                _ => (if nested_ty_kind.is_primitive() { ConversionExpressionKind::PrimitiveOpt } else { ConversionExpressionKind::ComplexOpt }, ffi_type)
                            };
                            Expression::cast_to(field_path, expr_kind, ffi_type, nested_ty)
                        }
                    }
                    TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::I128(..))) =>
                        Expression::cast_to::<Type, Type>(if is_ref { field_path.cloned() } else { field_path }, ConversionExpressionKind::Complex, parse_quote!([u8; 16]), parse_quote!(i128)),
                    TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::U128(..))) =>
                        Expression::cast_to::<Type, Type>(if is_ref { field_path.cloned() } else { field_path }, ConversionExpressionKind::Complex, parse_quote!([u8; 16]), parse_quote!(u128)),
                    TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::Str(TypeModel { ty, .. }))) =>
                        Expression::cast_to::<Type, Type>(field_path, ConversionExpressionKind::Complex, ffi_type, ty.joined_ref()),
                    TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::SmartPointer(SmartPointerModelKind::Box(TypeModel { ref ty, .. })))) => if let Some(nested_ty) = ty.maybe_first_nested_type_ref() {
                        match FFISpecialTypeResolve::<SPEC>::maybe_special_type(nested_ty, source) {
                            Some(SpecialType::Opaque(..)) =>
                                Expression::deref_expr(field_path),
                            _ =>
                                Expression::cast_to(Expression::deref_expr(field_path), ConversionExpressionKind::Complex, ffi_type, nested_ty.clone())
                        }
                    } else {
                        Expression::expression_to(ConversionExpressionKind::Primitive, field_path)
                    },
                    TypeModelKind::Dictionary(DictTypeModelKind::NonPrimitiveFermentable(DictFermentableModelKind::Cow(TypeModel { ref ty, .. }))) => if let Some(nested_ty) = ty.maybe_first_nested_type_ref() {
                        match (FFISpecialTypeResolve::<SPEC>::maybe_special_type(nested_ty, source), nested_ty.maybe_object(source)) {
                            (Some(SpecialType::Opaque(..)), ..) =>
                                Expression::boxed(Expression::cow_into_owned(field_path)),
                            (_, Some(ObjectKind::Type(TypeModelKind::Dictionary(DictTypeModelKind::Primitive(..))))) =>
                                Expression::cast_to(Expression::cow_into_owned(field_path), ConversionExpressionKind::Primitive, ffi_type, nested_ty.clone()),
                            _ =>
                                Expression::cast_to(Expression::cow_into_owned(field_path), ConversionExpressionKind::Complex, ffi_type, nested_ty.clone())
                        }
                    } else {
                        Expression::expression_to(ConversionExpressionKind::Primitive, Expression::cow_into_owned(field_path))
                    },
                    TypeModelKind::Bounds(model) => match model.chain.len() {
                        0 => field_path,
                        1 => if let Some(first_bound) = model.first_bound() {
                            if let Some(lambda_args) = MaybeLambdaArgs::<SPEC>::maybe_lambda_arg_names(first_bound) {
                                Expression::from_lambda(field_path, lambda_args)
                            } else {
                                Expression::cast_to(field_path, ConversionExpressionKind::Complex, ffi_type, full_type)
                            }
                        } else {
                            Expression::cast_to(field_path, ConversionExpressionKind::Complex, ffi_type, full_type)
                        }
                        _ =>
                            Expression::cast_to(field_path, ConversionExpressionKind::Complex, ffi_type, full_type)
                    },
                    _ =>
                        Expression::cast_to(if is_ref { field_path.cloned() } else { field_path }, ConversionExpressionKind::from(&full_type), ffi_type, full_type)
                }
            }
            _ =>
                Expression::cast_to(if is_ref { field_path.cloned() } else { field_path }, ConversionExpressionKind::from(&full_type), ffi_type, full_type)
        };
        result
    }
}