use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use syn::{parse_quote, ParenthesizedGenericArguments, Path, PathArguments, PathSegment, Type, TypePath, TypePtr, TypeReference};
use quote::ToTokens;
use proc_macro2::TokenStream as TokenStream2;
use crate::ast::CommaPunctuated;
pub use crate::composable::{GenericBoundsModel, TypeModel};
use crate::composable::{NestedArgument, TraitModel, TypeModeled};
use crate::composer::CommaPunctuatedNestedArguments;
use crate::context::ScopeContext;
use crate::kind::dict_type_model::DictTypeModelKind;
use crate::ext::{AsType, DictionaryType, MaybeLambdaArgs, Pop, ResolveTrait, ToType};
use crate::lang::{NameComposable, Specification};
#[derive(Clone)]
pub enum TypeModelKind {
Dictionary(DictTypeModelKind),
Trait(TraitModel),
TraitType(TypeModel),
Object(TypeModel),
Optional(TypeModel),
FnPointer(TypeModel),
Bounds(GenericBoundsModel),
Fn(TypeModel),
Array(TypeModel),
Slice(TypeModel),
Tuple(TypeModel),
Unknown(TypeModel),
Imported(TypeModel, Path),
}
impl TypeModeled for TypeModelKind {
fn type_model_mut(&mut self) -> &mut TypeModel {
match self {
TypeModelKind::TraitType(model) |
TypeModelKind::Object(model, ..) |
TypeModelKind::Optional(model, ..) |
TypeModelKind::FnPointer(model) |
TypeModelKind::Array(model) |
TypeModelKind::Slice(model) |
TypeModelKind::Tuple(model) |
TypeModelKind::Imported(model, ..) |
TypeModelKind::Unknown(model, ..) |
TypeModelKind::Fn(model, ..) => model,
TypeModelKind::Trait(model) => model.type_model_mut(),
TypeModelKind::Bounds(model) => model.type_model_mut(),
TypeModelKind::Dictionary(kind) => kind.type_model_mut()
}
}
fn type_model_ref(&self) -> &TypeModel {
match self {
TypeModelKind::TraitType(model) |
TypeModelKind::Object(model, ..) |
TypeModelKind::Optional(model, ..) |
TypeModelKind::FnPointer(model) |
TypeModelKind::Unknown(model, ..) |
TypeModelKind::Array(model) |
TypeModelKind::Slice(model) |
TypeModelKind::Tuple(model) |
TypeModelKind::Imported(model, ..) |
TypeModelKind::Fn(model, ..) => model,
TypeModelKind::Trait(model) => model.type_model_ref(),
TypeModelKind::Bounds(model) => model.type_model_ref(),
TypeModelKind::Dictionary(kind) => kind.type_model_ref()
}
}
}
impl ToTokens for TypeModelKind {
fn to_tokens(&self, tokens: &mut TokenStream2) {
self.to_type().to_tokens(tokens)
}
}
impl<SPEC> MaybeLambdaArgs<SPEC> for TypeModelKind
where SPEC: Specification {
fn maybe_lambda_arg_names(&self) -> Option<CommaPunctuated<SPEC::Name>> {
match self.maybe_callback() {
Some(ParenthesizedGenericArguments { inputs, ..}) =>
Some(CommaPunctuated::from_iter(inputs.iter().enumerate().map(|(index, _ty)| SPEC::Name::unnamed_arg(index)))),
_ => None
}
}
}
impl TypeModelKind {
pub fn unknown_type(ty: Type) -> Self {
Self::Unknown(TypeModel::new_default(ty))
}
pub fn unknown_type_with_nested_arguments(ty: Type, nested_arguments: CommaPunctuatedNestedArguments) -> Self {
Self::Unknown(TypeModel::new_nested(ty, nested_arguments))
}
pub fn unknown_type_ref(ty: &Type) -> Self {
Self::Unknown(TypeModel::from(ty))
}
pub fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown(_))
}
pub fn is_dictionary_opaque(&self) -> bool {
matches!(self, Self::Dictionary(DictTypeModelKind::NonPrimitiveOpaque(..)))
}
pub fn is_imported(&self) -> bool {
matches!(self, Self::Imported(..))
}
pub fn is_bounds(&self) -> bool {
matches!(self, Self::Bounds(..))
}
pub fn is_lambda(&self) -> bool {
matches!(self, Self::Dictionary(DictTypeModelKind::LambdaFn(..)))
}
pub fn is_optional(&self) -> bool {
matches!(self, Self::Optional(..))
}
pub(crate) fn maybe_trait_object_maybe_model_kind_or_same(&self, source: &ScopeContext) -> TypeModelKind {
match self {
TypeModelKind::Trait(model) =>
model.as_type().maybe_trait_object_model_kind(source),
_ =>
Some(self.clone()),
}.unwrap_or_else(|| self.clone())
}
pub(crate) fn maybe_trait_model(&self) -> Option<&TraitModel> {
match self {
TypeModelKind::Trait(model) => Some(model),
_ => None
}
}
pub(crate) fn maybe_trait_object_maybe_model_kind(&self, source: &ScopeContext) -> Option<Option<TypeModelKind>> {
match self {
TypeModelKind::Trait(model) => model.as_type().maybe_trait_object_maybe_model_kind(source),
_ => None
}
}
pub fn maybe_trait_model_kind_or_same(&self, source: &ScopeContext) -> Option<TypeModelKind> {
self.maybe_trait_object_maybe_model_kind(source)
.unwrap_or_else(|| Some(self.clone()))
}
pub fn is_refined(&self) -> bool {
match self {
TypeModelKind::Imported(..) |
TypeModelKind::Unknown(..) => false,
other => !other.nested_arguments_ref().iter().any(NestedArgument::is_refined),
}
}
pub fn maybe_callback(&self) -> Option<&ParenthesizedGenericArguments> {
if let TypeModelKind::FnPointer(ty, ..) | TypeModelKind::Dictionary(DictTypeModelKind::LambdaFn(ty)) = self {
if let Type::Path(TypePath { path: Path { segments, .. }, .. }) = ty.as_type() {
if let Some(PathSegment { arguments, ident, ..}) = segments.last() {
if ident.is_lambda_fn() {
if let PathArguments::Parenthesized(args) = arguments {
return Some(args)
}
}
}
}
}
None
}
}
impl<'a> AsType<'a> for TypeModelKind {
fn as_type(&'a self) -> &'a Type {
match self {
TypeModelKind::Bounds(model) => model.as_type(),
TypeModelKind::Dictionary(kind) => kind.as_type(),
TypeModelKind::TraitType(model) |
TypeModelKind::Object(model) |
TypeModelKind::Optional(model) |
TypeModelKind::FnPointer(model) |
TypeModelKind::Fn(model) |
TypeModelKind::Array(model) |
TypeModelKind::Slice(model) |
TypeModelKind::Tuple(model) |
TypeModelKind::Unknown(model) => model.as_type(),
TypeModelKind::Trait(model) => model.as_type(),
TypeModelKind::Imported(model, _) => model.as_type(),
}
}
}
impl ToType for TypeModelKind {
fn to_type(&self) -> Type {
match self {
TypeModelKind::Imported(ty, import_path) => {
let ty = ty.as_type();
let path = import_path.popped();
match ty {
Type::Reference(TypeReference { elem, mutability, .. }) =>
parse_quote!(&#mutability #path::#elem),
Type::Ptr(TypePtr { elem, mutability: Some(..), .. }) =>
parse_quote!(*mut #path::#elem),
Type::Ptr(TypePtr { elem, const_token: Some(..), .. }) =>
parse_quote!(*const #path::#elem),
_ =>
parse_quote!(#path::#ty)
}
},
_ => self.as_type().clone()
}
}
}
impl Debug for TypeModelKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", match self {
TypeModelKind::Trait(model) =>
format!("Trait({model})"),
TypeModelKind::Object(ty) =>
format!("Object({ty})"),
TypeModelKind::Optional(ty) =>
format!("Optional({ty})"),
TypeModelKind::Unknown(ty) =>
format!("Unknown({ty})"),
TypeModelKind::TraitType(ty) =>
format!("TraitType({ty})"),
TypeModelKind::Bounds(gbc) =>
format!("Bounds({gbc})"),
TypeModelKind::Fn(ty) =>
format!("Fn({ty})"),
TypeModelKind::Imported(ty, import_path) =>
format!("Imported({ty}, {})", import_path.to_token_stream()),
TypeModelKind::Array(ty) =>
format!("Array({ty})"),
TypeModelKind::Slice(ty) =>
format!("Slice({ty})"),
TypeModelKind::Tuple(ty) =>
format!("Tuple({ty})"),
TypeModelKind::FnPointer(ty) =>
format!("FnPointer({ty})"),
TypeModelKind::Dictionary(ty) =>
format!("Dictionary({ty})"),
}))
}
}
impl Display for TypeModelKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl PartialEq for TypeModelKind {
fn eq(&self, other: &Self) -> bool {
let self_tokens = [self.to_type().to_token_stream()];
let other_tokens = [other.to_type().to_token_stream()];
self_tokens.iter()
.map(|t| t.to_string())
.zip(other_tokens.iter().map(|t| t.to_string()))
.all(|(a, b)| a == b)
}
}
impl Eq for TypeModelKind {}
impl Hash for TypeModelKind {
fn hash<H: Hasher>(&self, state: &mut H) {
self.to_type().to_token_stream().to_string().hash(state);
}
}