diff options
Diffstat (limited to 'vendor/clap_derive/src/utils/ty.rs')
-rw-r--r-- | vendor/clap_derive/src/utils/ty.rs | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/vendor/clap_derive/src/utils/ty.rs b/vendor/clap_derive/src/utils/ty.rs new file mode 100644 index 0000000..9349bc2 --- /dev/null +++ b/vendor/clap_derive/src/utils/ty.rs @@ -0,0 +1,165 @@ +//! Special types handling + +use super::spanned::Sp; + +use syn::{ + spanned::Spanned, GenericArgument, Path, PathArguments, PathArguments::AngleBracketed, + PathSegment, Type, TypePath, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Ty { + Unit, + Vec, + VecVec, + Option, + OptionOption, + OptionVec, + OptionVecVec, + Other, +} + +impl Ty { + pub fn from_syn_ty(ty: &syn::Type) -> Sp<Self> { + use self::Ty::*; + let t = |kind| Sp::new(kind, ty.span()); + + if is_unit_ty(ty) { + t(Unit) + } else if let Some(vt) = get_vec_ty(ty, Vec, VecVec) { + t(vt) + } else if let Some(subty) = subty_if_name(ty, "Option") { + if is_generic_ty(subty, "Option") { + t(OptionOption) + } else if let Some(vt) = get_vec_ty(subty, OptionVec, OptionVecVec) { + t(vt) + } else { + t(Option) + } + } else { + t(Other) + } + } + + pub fn as_str(&self) -> &'static str { + match self { + Self::Unit => "()", + Self::Vec => "Vec<T>", + Self::Option => "Option<T>", + Self::OptionOption => "Option<Option<T>>", + Self::OptionVec => "Option<Vec<T>>", + Self::VecVec => "Vec<Vec<T>>", + Self::OptionVecVec => "Option<Vec<Vec<T>>>", + Self::Other => "...other...", + } + } +} + +pub fn inner_type(field_ty: &syn::Type) -> &syn::Type { + let ty = Ty::from_syn_ty(field_ty); + match *ty { + Ty::Vec | Ty::Option => sub_type(field_ty).unwrap_or(field_ty), + Ty::OptionOption | Ty::OptionVec | Ty::VecVec => { + sub_type(field_ty).and_then(sub_type).unwrap_or(field_ty) + } + Ty::OptionVecVec => sub_type(field_ty) + .and_then(sub_type) + .and_then(sub_type) + .unwrap_or(field_ty), + _ => field_ty, + } +} + +pub fn sub_type(ty: &syn::Type) -> Option<&syn::Type> { + subty_if(ty, |_| true) +} + +fn only_last_segment(mut ty: &syn::Type) -> Option<&PathSegment> { + while let syn::Type::Group(syn::TypeGroup { elem, .. }) = ty { + ty = elem; + } + match ty { + Type::Path(TypePath { + qself: None, + path: + Path { + leading_colon: None, + segments, + }, + }) => only_one(segments.iter()), + + _ => None, + } +} + +fn subty_if<F>(ty: &syn::Type, f: F) -> Option<&syn::Type> +where + F: FnOnce(&PathSegment) -> bool, +{ + only_last_segment(ty) + .filter(|segment| f(segment)) + .and_then(|segment| { + if let AngleBracketed(args) = &segment.arguments { + only_one(args.args.iter()).and_then(|genneric| { + if let GenericArgument::Type(ty) = genneric { + Some(ty) + } else { + None + } + }) + } else { + None + } + }) +} + +pub fn subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type> { + subty_if(ty, |seg| seg.ident == name) +} + +pub fn is_simple_ty(ty: &syn::Type, name: &str) -> bool { + only_last_segment(ty) + .map(|segment| { + if let PathArguments::None = segment.arguments { + segment.ident == name + } else { + false + } + }) + .unwrap_or(false) +} + +fn is_generic_ty(ty: &syn::Type, name: &str) -> bool { + subty_if_name(ty, name).is_some() +} + +fn is_unit_ty(ty: &syn::Type) -> bool { + if let syn::Type::Tuple(tuple) = ty { + tuple.elems.is_empty() + } else { + false + } +} + +fn only_one<I, T>(mut iter: I) -> Option<T> +where + I: Iterator<Item = T>, +{ + iter.next().filter(|_| iter.next().is_none()) +} + +#[cfg(feature = "unstable-v5")] +fn get_vec_ty(ty: &Type, vec_ty: Ty, vecvec_ty: Ty) -> Option<Ty> { + subty_if_name(ty, "Vec").map(|subty| { + if is_generic_ty(subty, "Vec") { + vecvec_ty + } else { + vec_ty + } + }) +} + +#[cfg(not(feature = "unstable-v5"))] +fn get_vec_ty(ty: &Type, vec_ty: Ty, _vecvec_ty: Ty) -> Option<Ty> { + is_generic_ty(ty, "Vec").then_some(vec_ty) +} |