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