serde_columnar_derive/
utils.rs

1#![allow(dead_code)]
2use proc_macro2::Ident;
3use quote::ToTokens;
4use syn::{parse_quote, GenericArgument, PathArguments, Type};
5
6// Whether the type looks like it might be `std::borrow::Cow<T>` where elem="T".
7// This can have false negatives and false positives.
8//
9// False negative:
10//
11//     use std::borrow::Cow as Pig;
12//
13//     #[derive(Deserialize)]
14//     struct S<'a> {
15//         #[serde(borrow)]
16//         pig: Pig<'a, str>,
17//     }
18//
19// False positive:
20//
21//     type str = [i16];
22//
23//     #[derive(Deserialize)]
24//     struct S<'a> {
25//         #[serde(borrow)]
26//         cow: Cow<'a, str>,
27//     }
28pub fn is_cow(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
29    let path = match ungroup(ty) {
30        syn::Type::Path(ty) => &ty.path,
31        _ => {
32            return false;
33        }
34    };
35    let seg = match path.segments.last() {
36        Some(seg) => seg,
37        None => {
38            return false;
39        }
40    };
41    let args = match &seg.arguments {
42        syn::PathArguments::AngleBracketed(bracketed) => &bracketed.args,
43        _ => {
44            return false;
45        }
46    };
47    seg.ident == "Cow"
48        && args.len() == 2
49        && match (&args[0], &args[1]) {
50            (syn::GenericArgument::Lifetime(_), syn::GenericArgument::Type(arg)) => elem(arg),
51            _ => false,
52        }
53}
54
55pub fn is_option(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
56    let path = match ungroup(ty) {
57        syn::Type::Path(ty) => &ty.path,
58        _ => {
59            return false;
60        }
61    };
62    let seg = match path.segments.last() {
63        Some(seg) => seg,
64        None => {
65            return false;
66        }
67    };
68    let args = match &seg.arguments {
69        syn::PathArguments::AngleBracketed(bracketed) => &bracketed.args,
70        _ => {
71            return false;
72        }
73    };
74    seg.ident == "Option"
75        && args.len() == 1
76        && match &args[0] {
77            syn::GenericArgument::Type(arg) => elem(arg),
78            _ => false,
79        }
80}
81
82// Whether the type looks like it might be `&T` where elem="T". This can have
83// false negatives and false positives.
84//
85// False negative:
86//
87//     type Yarn = str;
88//
89//     #[derive(Deserialize)]
90//     struct S<'a> {
91//         r: &'a Yarn,
92//     }
93//
94// False positive:
95//
96//     type str = [i16];
97//
98//     #[derive(Deserialize)]
99//     struct S<'a> {
100//         r: &'a str,
101//     }
102pub fn is_reference(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
103    match ungroup(ty) {
104        syn::Type::Reference(ty) => ty.mutability.is_none() && elem(&ty.elem),
105        _ => false,
106    }
107}
108
109pub fn is_str(ty: &syn::Type) -> bool {
110    is_primitive_type(ty, "str")
111}
112
113pub fn is_slice_u8(ty: &syn::Type) -> bool {
114    match ungroup(ty) {
115        syn::Type::Slice(ty) => is_primitive_type(&ty.elem, "u8"),
116        _ => false,
117    }
118}
119
120pub fn is_primitive_type(ty: &syn::Type, primitive: &str) -> bool {
121    match ungroup(ty) {
122        syn::Type::Path(ty) => ty.qself.is_none() && is_primitive_path(&ty.path, primitive),
123        _ => false,
124    }
125}
126
127pub fn is_primitive_path(path: &syn::Path, primitive: &str) -> bool {
128    path.leading_colon.is_none()
129        && path.segments.len() == 1
130        && path.segments[0].ident == primitive
131        && path.segments[0].arguments.is_empty()
132}
133
134pub fn ungroup(mut ty: &Type) -> &Type {
135    while let Type::Group(group) = ty {
136        ty = &group.elem;
137    }
138    ty
139}
140
141pub fn add_lifetime_to_type<F>(
142    ty: &mut Type,
143    lifetime: GenericArgument,
144    map_name_fn: Option<F>,
145) -> syn::Result<()>
146where
147    F: FnMut(&mut Ident),
148{
149    match ty {
150        Type::Path(type_path) => {
151            let last_segment = type_path
152                .path
153                .segments
154                .last_mut()
155                .expect("Expected at least one segment");
156
157            match &mut last_segment.arguments {
158                PathArguments::AngleBracketed(angle_bracketed) => {
159                    angle_bracketed.args.insert(0, lifetime);
160                }
161                PathArguments::None => {
162                    let args = vec![lifetime];
163                    last_segment.arguments =
164                        PathArguments::AngleBracketed(parse_quote!(<#(#args),*>));
165                }
166                _ => {
167                    return Err(syn::Error::new_spanned(
168                        ty.into_token_stream(),
169                        "the field type cannot add lifetime",
170                    ))
171                }
172            };
173
174            if let Some(mut map_name_fn) = map_name_fn {
175                let ident = &mut last_segment.ident;
176                map_name_fn(ident);
177            }
178
179            Ok(())
180        }
181        _ => Err(syn::Error::new_spanned(
182            ty.into_token_stream(),
183            "the field type need be Path",
184        )),
185    }
186}