mirror of
https://github.com/clap-rs/clap
synced 2025-01-09 19:28:45 +00:00
109 lines
2.5 KiB
Rust
109 lines
2.5 KiB
Rust
|
//! Special types handling
|
||
|
|
||
|
use super::spanned::Sp;
|
||
|
|
||
|
use syn::{
|
||
|
spanned::Spanned, GenericArgument, Path, PathArguments, PathArguments::AngleBracketed,
|
||
|
PathSegment, Type, TypePath,
|
||
|
};
|
||
|
|
||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||
|
pub enum Ty {
|
||
|
Bool,
|
||
|
Vec,
|
||
|
Option,
|
||
|
OptionOption,
|
||
|
OptionVec,
|
||
|
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_simple_ty(ty, "bool") {
|
||
|
t(Bool)
|
||
|
} else if is_generic_ty(ty, "Vec") {
|
||
|
t(Vec)
|
||
|
} else if let Some(subty) = subty_if_name(ty, "Option") {
|
||
|
if is_generic_ty(subty, "Option") {
|
||
|
t(OptionOption)
|
||
|
} else if is_generic_ty(subty, "Vec") {
|
||
|
t(OptionVec)
|
||
|
} else {
|
||
|
t(Option)
|
||
|
}
|
||
|
} else {
|
||
|
t(Other)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn sub_type(ty: &syn::Type) -> Option<&syn::Type> {
|
||
|
subty_if(ty, |_| true)
|
||
|
}
|
||
|
|
||
|
fn only_last_segment(ty: &syn::Type) -> Option<&PathSegment> {
|
||
|
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
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
fn subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type> {
|
||
|
subty_if(ty, |seg| seg.ident == name)
|
||
|
}
|
||
|
|
||
|
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 only_one<I, T>(mut iter: I) -> Option<T>
|
||
|
where
|
||
|
I: Iterator<Item = T>,
|
||
|
{
|
||
|
iter.next().filter(|_| iter.next().is_none())
|
||
|
}
|