ferment-sys 0.2.14

Syntax tree morphing of FFI-compatible stuff
Documentation
use std::fmt::{Display, Formatter};
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{Path, TraitBound, Type, TypePtr, TypeReference, TypeTraitObject};
use crate::ext::{MaybeTraitBound, ToType};
use crate::lang::Specification;
use crate::presentation::FFIVariable;

#[derive(Clone, Debug)]
pub enum ScopeSearchKey {
    Path(Path, Option<Box<ScopeSearchKey>>),
    Type(Type, Option<Box<ScopeSearchKey>>)
}
impl Display for ScopeSearchKey {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Path(path, original) =>
                f.write_fmt(format_args!("Path({}, {})", path.to_token_stream(), original.as_ref().map(|d| d.to_string()).unwrap_or_default())),
            Self::Type(ty, original) =>
                f.write_fmt(format_args!("Type({}, {})", ty.to_token_stream(), original.as_ref().map(|d| d.to_string()).unwrap_or_default())),
        }
    }
}

impl ToTokens for ScopeSearchKey {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::Path(path, ..) => path.to_tokens(tokens),
            Self::Type(ty, ..) => ty.to_tokens(tokens)
        }
    }
}

impl ToType for ScopeSearchKey {
    fn to_type(&self) -> Type {
        match self {
            ScopeSearchKey::Path(path, ..) => path.to_type(),
            ScopeSearchKey::Type(ty, ..) => ty.clone()
        }
    }
}

impl ScopeSearchKey {
    pub fn ptr_composer<SPEC, T>(&self) -> fn(T) -> FFIVariable<SPEC, T>
    where SPEC: Specification,
          T: ToTokens {
        if self.maybe_originally_is_const_ptr() {
            FFIVariable::const_ptr
        } else {
            FFIVariable::mut_ptr
        }
    }
}


impl ScopeSearchKey {
    pub fn maybe_original_key(&self) -> &Option<Box<ScopeSearchKey>> {
        match self {
            ScopeSearchKey::Path(_, original) |
            ScopeSearchKey::Type(_, original) => original
        }
    }
    pub fn maybe_originally_is_ptr(&self) -> bool {
        self.maybe_original_key().as_ref().is_some_and(|boxed_key| boxed_key.is_const_ptr() || boxed_key.is_mut_ptr())
    }
    pub fn maybe_originally_is_const_ptr(&self) -> bool {
        self.maybe_original_key().as_ref().is_some_and(|boxed_key| boxed_key.is_const_ptr())
    }
    pub fn maybe_originally_is_mut_ptr(&self) -> bool {
        self.maybe_original_key().as_ref().is_some_and(|boxed_key| boxed_key.is_mut_ptr())
    }
    pub fn maybe_originally_is_ref(&self) -> bool {
        self.maybe_original_key().as_ref().is_some_and(|boxed_key| boxed_key.is_ref())
    }
    pub fn maybe_originally_is_mut_ref(&self) -> bool {
        self.maybe_original_key().as_ref().is_some_and(|boxed_key| boxed_key.is_mut_ref())
    }
    pub fn maybe_originally_is_dyn(&self) -> bool {
        self.maybe_original_key().as_ref().is_some_and(|boxed_key| boxed_key.is_dyn())
    }
    pub fn maybe_ptr(&self) -> Option<&TypePtr> {
        match self {
            ScopeSearchKey::Type(Type::Ptr(type_ptr), ..) => Some(type_ptr),
            _ => None
        }
    }
    pub fn maybe_dyn(&self) -> Option<&TypeTraitObject> {
        match self {
            ScopeSearchKey::Type(Type::TraitObject(ty), ..) => Some(ty),
            _ => None
        }
    }
    pub fn maybe_ref(&self) -> Option<&TypeReference> {
        match self {
            ScopeSearchKey::Type(Type::Reference(type_ref), ..) => Some(type_ref),
            _ => None
        }
    }
    pub fn is_const_ptr(&self) -> bool {
        self.maybe_ptr().is_some_and(|ty| ty.const_token.is_some())
    }
    pub fn is_mut_ptr(&self) -> bool {
        self.maybe_ptr().is_some_and(|ty| ty.mutability.is_some())
    }
    pub fn is_ref(&self) -> bool {
        self.maybe_ref().is_some()
    }
    pub fn is_mut_ref(&self) -> bool {
        matches!(self, ScopeSearchKey::Type(Type::Reference(TypeReference { mutability: Some(_), .. }), ..))
    }
    pub fn is_dyn(&self) -> bool {
        matches!(self, ScopeSearchKey::Type(Type::TraitObject(TypeTraitObject { .. }), ..))
    }
}

impl ScopeSearchKey {
    pub fn trait_bound(trait_bound: &TraitBound, original: Self) -> Self {
        Self::Path(trait_bound.path.clone(), Some(Box::new(original)))
    }
    pub fn r#type(ty: &Type, original: Self) -> Self {
        Self::Type(ty.clone(), Some(Box::new(original)))
    }
    pub fn maybe_from_ref(ty: &Type) -> Option<Self> {
        let original = ScopeSearchKey::Type(ty.clone(), None);
        match ty {
            Type::TraitObject(TypeTraitObject { bounds , ..}) =>
                bounds.first()
                    .and_then(MaybeTraitBound::maybe_trait_bound)
                    .map(|trait_bound| ScopeSearchKey::trait_bound(trait_bound, original)),
            Type::Reference(TypeReference { elem, .. }) |
            Type::Ptr(TypePtr { elem, .. }) =>
                Some(ScopeSearchKey::r#type(elem, original)),
            ty =>
                Some(ScopeSearchKey::r#type(ty, original)),
        }

    }
    pub fn maybe_from(ty: Type) -> Option<Self> {
        let original = ScopeSearchKey::Type(ty.clone(), None);
        match ty {
            Type::TraitObject(TypeTraitObject { bounds , .. }) => match bounds.len() {
                1 => bounds.first()
                    .and_then(MaybeTraitBound::maybe_trait_bound)
                    .map(|trait_bound| ScopeSearchKey::trait_bound(trait_bound, original)),
                _ => None
            },
            Type::Reference(TypeReference { elem, .. }) |
            Type::Ptr(TypePtr { elem, .. }) =>
                Some(ScopeSearchKey::Type(*elem, Some(Box::new(original)))),
            ty =>
                Some(ScopeSearchKey::Type(ty, Some(Box::new(original)))),
        }
    }
    pub fn find<K, T: Fn(&Type) -> K>(&self, finder: T) -> K {
        match self {
            ScopeSearchKey::Path(ref path, ..) => finder(&path.to_type()),
            ScopeSearchKey::Type(ty, ..) => finder(ty),
        }
    }
}