ferment-sys 0.2.14

Syntax tree morphing of FFI-compatible stuff
Documentation
use quote::{quote, ToTokens};
use syn::{parse_quote, BareFnArg, Lifetime, ParenthesizedGenericArguments, ReturnType, Type, TypeBareFn};
use crate::ast::{CommaPunctuated, Depunctuated};
use crate::composable::FieldComposer;
use crate::composer::{AspectPresentable, AttrComposable, GenericComposerInfo, SourceComposable, ConversionToComposer, CallbackComposer, VarComposer};
use crate::context::ScopeContext;
use crate::kind::{CallbackKind, FieldTypeKind, GenericTypeKind, SpecialType, TypeKind};
use crate::ext::{Accessory, FFISpecialTypeResolve, FFIVarResolve, GenericNestedArg, LifetimeProcessor, Mangle, MaybeParenthesizedArgs, MaybeTraitBound, PunctuateOne, Resolve, ToType, WrapIntoRoundBraces};
use crate::lang::RustSpecification;
use crate::presentable::{Aspect, ScopeContextPresentable};
use crate::presentation::{ArgPresentation, DictionaryExpr, DictionaryName, InterfacePresentation, Name};

impl SourceComposable for CallbackComposer<RustSpecification> {
    type Source = ScopeContext;
    type Output = Option<GenericComposerInfo<RustSpecification>>;

    fn compose(&self, source: &Self::Source) -> Self::Output {
        let Self { kind, .. } = self;
        let mut lifetimes = Vec::<Lifetime>::new();
        let (inputs, output) = match kind {
            CallbackKind::FnOnce(Type::TraitObject(trait_object)) |
            CallbackKind::Fn(Type::TraitObject(trait_object)) |
            CallbackKind::FnMut(Type::TraitObject(trait_object)) =>
                trait_object.bounds.iter()
                    .find_map(MaybeTraitBound::maybe_trait_bound)
                    .and_then(|trait_bound| trait_bound.path.segments.last())
                    .and_then(MaybeParenthesizedArgs::maybe_parenthesized_args)
                    .map(|ParenthesizedGenericArguments { inputs, output, .. }| (inputs.clone(), output.clone()))?,
            CallbackKind::FnPointer(Type::BareFn(TypeBareFn { inputs, output, .. })) =>
                (inputs.iter().map(|b| b.ty.clone()).collect(), output.clone()),
            CallbackKind::FnOnce(Type::Path(path)) |
            CallbackKind::Fn(Type::Path(path)) |
            CallbackKind::FnMut(Type::Path(path)) |
            CallbackKind::FnPointer(Type::Path(path)) => {
                let ParenthesizedGenericArguments { inputs, output, .. } = path.path.segments.last()?.maybe_parenthesized_args()?;
                (inputs.clone(), output.clone())
            }
            _ => panic!("Unsupported callback kind: {kind:?}")
        };
        let ffi_result = DictionaryName::FFiResult;
        let (return_type, from_result_conversion, dtor_arg) = match output {
            ReturnType::Type(token, field_type) => {
                let full_ty: Type = field_type.resolve(source);
                lifetimes.extend(field_type.unique_lifetimes());
                let (ffi_ty, from_result_conversion) = match TypeKind::from(&full_ty) {
                    TypeKind::Primitive(_) => (full_ty.clone(), DictionaryExpr::DictionaryName(ffi_result)),
                    TypeKind::Complex(ty) => {
                        let ffi_ty = FFIVarResolve::<RustSpecification>::special_or_to_ffi_full_path_type(&ty, source);
                        (ffi_ty.joined_mut(), match FFISpecialTypeResolve::<RustSpecification>::maybe_special_type(&ty, source) {
                            Some(SpecialType::Opaque(..)) =>
                                DictionaryExpr::Clone(quote!((&*#ffi_result))),
                            _ =>
                                DictionaryExpr::callback_dtor(DictionaryExpr::casted_ffi_conversion_from(&ffi_ty, &ty, &ffi_result), &ffi_result)
                        })
                    },
                    TypeKind::Generic(generic_ty) => match generic_ty {
                        GenericTypeKind::Optional(ty) => match ty.maybe_first_nested_type_kind().unwrap() {
                            TypeKind::Primitive(ty) => (ty.joined_mut(), DictionaryExpr::IfElse(quote!(#ffi_result.is_null()), quote!(None), quote!(*#ffi_result))),
                            TypeKind::Complex(ty) => {
                                let maybe_special: Option<SpecialType<RustSpecification>> = ty.maybe_special_type(source);
                                let ffi_ty = FFIVarResolve::<RustSpecification>::special_or_to_ffi_full_path_type(&ty, source);
                                (ffi_ty.joined_mut(), DictionaryExpr::IfElse(quote!(#ffi_result.is_null()), quote!(None), match maybe_special {
                                    Some(SpecialType::Opaque(..)) =>
                                        DictionaryExpr::some(DictionaryExpr::Clone(WrapIntoRoundBraces::wrap(DictionaryExpr::deref_ref(&ffi_result)))),
                                    _ =>
                                        DictionaryExpr::callback_dtor(DictionaryExpr::casted_ffi_conversion_from_opt(&ffi_ty, &ty, &ffi_result), &ffi_result)
                                }.to_token_stream()))
                            },
                            TypeKind::Generic(ty) => {
                                let ffi_ty = FFIVarResolve::<RustSpecification>::special_or_to_ffi_full_path_type(&ty, source);
                                (ffi_ty.joined_mut(), DictionaryExpr::callback_dtor(DictionaryExpr::casted_ffi_conversion_from_opt(&ffi_ty, ty.ty(), &ffi_result), &ffi_result))
                            },
                        },
                        GenericTypeKind::TraitBounds(_) => unimplemented!("TODO: mixins+traits+generics"),
                        _ => {
                            let ffi_ty = FFIVarResolve::<RustSpecification>::special_or_to_ffi_full_path_type(&full_ty, source);
                            (ffi_ty.joined_mut(), DictionaryExpr::callback_dtor(DictionaryExpr::casted_ffi_conversion_from(&ffi_ty, &generic_ty, &ffi_result), &ffi_result))
                        }
                    }
                };
                (ReturnType::Type(token, Box::new(full_ty)), from_result_conversion, Some(ffi_ty))
            },
            ReturnType::Default => (ReturnType::Default, DictionaryExpr::DictionaryName(ffi_result), None),
        };
        let mut args = CommaPunctuated::new();
        let mut ffi_args = CommaPunctuated::new();
        let mut arg_to_conversions = CommaPunctuated::new();
        inputs
            .iter()
            .enumerate()
            .for_each(|(index, ty)| {
                let name = Name::UnnamedArg(index);
                lifetimes.extend(ty.unique_lifetimes());
                args.push(ArgPresentation::inherited_field(&[], name.mangle_ident_default(), ty.clone()));
                ffi_args.push(bare_fn_arg(VarComposer::<RustSpecification>::value(ty).compose(source).to_type()));
                arg_to_conversions.push(ConversionToComposer::<RustSpecification>::value(name, ty).compose(source).present(source));
            });
        let ffi_type = self.present_ffi_aspect();
        let attrs = self.compose_attributes();
        Some(GenericComposerInfo::<RustSpecification>::callback(
            Aspect::raw_struct_ident(kind.mangle_ident_default()),
            &attrs,
            if let Some(dtor_arg) = dtor_arg {
                Depunctuated::from_iter([
                    FieldComposer::named_no_attrs(Name::caller(), FieldTypeKind::Type(bare(ffi_args, ReturnType::Type(Default::default(), Box::new(dtor_arg.clone()))))),
                    FieldComposer::named_no_attrs(Name::destructor(), FieldTypeKind::Type(bare(bare_fn_arg(dtor_arg).punctuate_one(), ReturnType::Default)))
                ])
            } else {
                FieldComposer::named_no_attrs(Name::caller(), FieldTypeKind::Type(bare(ffi_args, ReturnType::Default))).punctuate_one()
            },
            Depunctuated::from_iter([
                InterfacePresentation::send_sync(&attrs, &ffi_type),
                InterfacePresentation::callback(&attrs, &lifetimes, ffi_type, args, return_type, arg_to_conversions, from_result_conversion),
            ])
        ))
    }
}

fn bare_fn_arg(ty: Type) -> BareFnArg {
    BareFnArg { attrs: vec![], name: None, ty }
}

fn bare(inputs: CommaPunctuated<BareFnArg>, output: ReturnType) -> Type {
    Type::BareFn(TypeBareFn {
        abi: Some(parse_quote!(extern "C")),
        inputs,
        output,
        lifetimes: None,
        unsafety: Some(Default::default()),
        fn_token: Default::default(),
        paren_token: Default::default(),
        variadic: None
    })
}