blob: ad3acd9d50306fdd765edf93c39c5ac576b2d2d7 [file] [log] [blame]
Matthew Maurerdc6194e2020-06-02 11:15:18 -07001//! Special types handling
2
3use crate::spanned::Sp;
4
5use syn::{
6 spanned::Spanned, GenericArgument, Path, PathArguments, PathArguments::AngleBracketed,
7 PathSegment, Type, TypePath,
8};
9
10#[derive(Copy, Clone, PartialEq, Debug)]
11pub enum Ty {
12 Bool,
13 Vec,
14 Option,
15 OptionOption,
16 OptionVec,
17 Other,
18}
19
20impl Ty {
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
43pub fn sub_type(ty: &syn::Type) -> Option<&syn::Type> {
44 subty_if(ty, |_| true)
45}
46
47fn 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
62fn subty_if<F>(ty: &syn::Type, f: F) -> Option<&syn::Type>
63where
64 F: FnOnce(&PathSegment) -> bool,
65{
Haibo Huangfef3b762020-10-28 22:33:57 -070066 let ty = strip_group(ty);
67
Matthew Maurerdc6194e2020-06-02 11:15:18 -070068 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
85pub 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
89pub fn is_simple_ty(ty: &syn::Type, name: &str) -> bool {
Haibo Huangfef3b762020-10-28 22:33:57 -070090 let ty = strip_group(ty);
91
Matthew Maurerdc6194e2020-06-02 11:15:18 -070092 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
Haibo Huangfef3b762020-10-28 22:33:57 -0700103// 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
112fn 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
Matthew Maurerdc6194e2020-06-02 11:15:18 -0700120fn is_generic_ty(ty: &syn::Type, name: &str) -> bool {
121 subty_if_name(ty, name).is_some()
122}
123
124fn only_one<I, T>(mut iter: I) -> Option<T>
125where
126 I: Iterator<Item = T>,
127{
128 iter.next().filter(|_| iter.next().is_none())
129}