1 //! Special types handling
2 
3 use crate::spanned::Sp;
4 
5 use syn::{
6     spanned::Spanned, GenericArgument, Path, PathArguments, PathArguments::AngleBracketed,
7     PathSegment, Type, TypePath,
8 };
9 
10 #[derive(Copy, Clone, PartialEq, Debug)]
11 pub enum Ty {
12     Bool,
13     Vec,
14     Option,
15     OptionOption,
16     OptionVec,
17     Other,
18 }
19 
20 impl Ty {
from_syn_ty(ty: &syn::Type) -> Sp<Self>21     pub fn from_syn_ty(ty: &syn::Type) -> Sp<Self> {
22         use Ty::*;
23         let t = |kind| Sp::new(kind, ty.span());
24 
25         if is_simple_ty(ty, "bool") {
26             t(Bool)
27         } else if is_generic_ty(ty, "Vec") {
28             t(Vec)
29         } else if let Some(subty) = subty_if_name(ty, "Option") {
30             if is_generic_ty(subty, "Option") {
31                 t(OptionOption)
32             } else if is_generic_ty(subty, "Vec") {
33                 t(OptionVec)
34             } else {
35                 t(Option)
36             }
37         } else {
38             t(Other)
39         }
40     }
41 }
42 
sub_type(ty: &syn::Type) -> Option<&syn::Type>43 pub fn sub_type(ty: &syn::Type) -> Option<&syn::Type> {
44     subty_if(ty, |_| true)
45 }
46 
only_last_segment(ty: &syn::Type) -> Option<&PathSegment>47 fn only_last_segment(ty: &syn::Type) -> Option<&PathSegment> {
48     match ty {
49         Type::Path(TypePath {
50             qself: None,
51             path:
52                 Path {
53                     leading_colon: None,
54                     segments,
55                 },
56         }) => only_one(segments.iter()),
57 
58         _ => None,
59     }
60 }
61 
subty_if<F>(ty: &syn::Type, f: F) -> Option<&syn::Type> where F: FnOnce(&PathSegment) -> bool,62 fn subty_if<F>(ty: &syn::Type, f: F) -> Option<&syn::Type>
63 where
64     F: FnOnce(&PathSegment) -> bool,
65 {
66     let ty = strip_group(ty);
67 
68     only_last_segment(ty)
69         .filter(|segment| f(segment))
70         .and_then(|segment| {
71             if let AngleBracketed(args) = &segment.arguments {
72                 only_one(args.args.iter()).and_then(|genneric| {
73                     if let GenericArgument::Type(ty) = genneric {
74                         Some(ty)
75                     } else {
76                         None
77                     }
78                 })
79             } else {
80                 None
81             }
82         })
83 }
84 
subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type>85 pub fn subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type> {
86     subty_if(ty, |seg| seg.ident == name)
87 }
88 
is_simple_ty(ty: &syn::Type, name: &str) -> bool89 pub fn is_simple_ty(ty: &syn::Type, name: &str) -> bool {
90     let ty = strip_group(ty);
91 
92     only_last_segment(ty)
93         .map(|segment| {
94             if let PathArguments::None = segment.arguments {
95                 segment.ident == name
96             } else {
97                 false
98             }
99         })
100         .unwrap_or(false)
101 }
102 
103 // If the struct is placed inside of a macro_rules! declaration,
104 // in some circumstances, the tokens inside will be enclosed
105 // in `proc_macro::Group` delimited by invisible `proc_macro::Delimiter::None`.
106 //
107 // In syn speak, this is encoded via `*::Group` variants. We don't really care about
108 // that, so let's just strip it.
109 //
110 // Details: https://doc.rust-lang.org/proc_macro/enum.Delimiter.html#variant.None
111 // See also: https://github.com/TeXitoi/structopt/issues/439
strip_group(mut ty: &syn::Type) -> &syn::Type112 fn strip_group(mut ty: &syn::Type) -> &syn::Type {
113     while let Type::Group(group) = ty {
114         ty = &*group.elem;
115     }
116 
117     ty
118 }
119 
is_generic_ty(ty: &syn::Type, name: &str) -> bool120 fn is_generic_ty(ty: &syn::Type, name: &str) -> bool {
121     subty_if_name(ty, name).is_some()
122 }
123 
only_one<I, T>(mut iter: I) -> Option<T> where I: Iterator<Item = T>,124 fn only_one<I, T>(mut iter: I) -> Option<T>
125 where
126     I: Iterator<Item = T>,
127 {
128     iter.next().filter(|_| iter.next().is_none())
129 }
130