use quote::ToTokens;
pub(crate) enum InjectionType {
Catalog,
CatalogRef,
CatalogWeakRef,
Arc { inner: syn::Type },
Reference { inner: syn::Type },
Option { element: Box<InjectionType> },
Vec { item: Box<InjectionType> },
Lazy { element: Box<InjectionType> },
Value { typ: syn::Type },
}
pub(crate) fn deduce_injection_type(typ: &syn::Type) -> InjectionType {
if is_catalog(typ) {
InjectionType::Catalog
} else if is_catalog_ref(typ) {
InjectionType::CatalogRef
} else if is_catalog_weak_ref(typ) {
InjectionType::CatalogWeakRef
} else if let Some(inner) = strip_reference(typ) {
InjectionType::Reference { inner }
} else if let Some(inner) = get_arc_element_type(typ) {
InjectionType::Arc { inner }
} else if let Some(elem_typ) = get_option_element_type(typ) {
InjectionType::Option {
element: Box::new(deduce_injection_type(&elem_typ)),
}
} else if let Some(elem_typ) = get_vec_item_type(typ) {
InjectionType::Vec {
item: Box::new(deduce_injection_type(&elem_typ)),
}
} else if let Some(elem_typ) = get_lazy_element_type(typ) {
InjectionType::Lazy {
element: Box::new(deduce_injection_type(&elem_typ)),
}
} else {
InjectionType::Value { typ: typ.clone() }
}
}
pub(crate) fn is_catalog(typ: &syn::Type) -> bool {
let syn::Type::Path(typepath) = typ else {
return false;
};
typepath.qself.is_none() && typepath.path.segments.last().unwrap().ident == "Catalog"
}
pub(crate) fn is_catalog_ref(typ: &syn::Type) -> bool {
match typ {
syn::Type::Reference(r) => match r.elem.as_ref() {
syn::Type::Path(type_path) => {
type_path.qself.is_none()
&& type_path.path.segments.last().unwrap().ident == "Catalog"
}
_ => false,
},
_ => false,
}
}
pub(crate) fn is_catalog_weak_ref(typ: &syn::Type) -> bool {
let syn::Type::Path(typepath) = typ else {
return false;
};
typepath.qself.is_none() && typepath.path.segments.last().unwrap().ident == "CatalogWeakRef"
}
pub(crate) fn strip_reference(typ: &syn::Type) -> Option<syn::Type> {
match typ {
syn::Type::Reference(r) => Some(r.elem.as_ref().clone()),
_ => None,
}
}
pub(crate) fn get_arc_element_type(typ: &syn::Type) -> Option<syn::Type> {
let syn::Type::Path(typepath) = typ else {
panic!("Expected a Type::Path");
};
if typepath.qself.is_some() || typepath.path.segments.last().unwrap().ident != "Arc" {
return None;
}
let syn::PathArguments::AngleBracketed(args) =
&typepath.path.segments.last().unwrap().arguments
else {
return None;
};
Some(syn::parse2(args.args.to_token_stream()).unwrap())
}
pub(crate) fn get_option_element_type(typ: &syn::Type) -> Option<syn::Type> {
let syn::Type::Path(typepath) = typ else {
panic!("Expected a Type::Path");
};
if typepath.qself.is_some() || &typepath.path.segments.last().unwrap().ident != "Option" {
return None;
}
let syn::PathArguments::AngleBracketed(args) =
&typepath.path.segments.last().unwrap().arguments
else {
return None;
};
Some(syn::parse2(args.args.to_token_stream()).unwrap())
}
pub(crate) fn get_lazy_element_type(typ: &syn::Type) -> Option<syn::Type> {
let syn::Type::Path(typepath) = typ else {
panic!("Expected a Type::Path");
};
if typepath.qself.is_some() || &typepath.path.segments.last().unwrap().ident != "Lazy" {
return None;
}
let syn::PathArguments::AngleBracketed(args) =
&typepath.path.segments.last().unwrap().arguments
else {
return None;
};
Some(syn::parse2(args.args.to_token_stream()).unwrap())
}
pub(crate) fn get_vec_item_type(typ: &syn::Type) -> Option<syn::Type> {
let syn::Type::Path(typepath) = typ else {
panic!("Expected a Type::Path");
};
if typepath.qself.is_some() || typepath.path.segments.last().unwrap().ident != "Vec" {
return None;
}
let syn::PathArguments::AngleBracketed(args) =
&typepath.path.segments.last().unwrap().arguments
else {
return None;
};
Some(syn::parse2(args.args.to_token_stream()).unwrap())
}