Skip to main content

ts_procmacro/
lib.rs

1//! # `ts-macro`
2
3use quote::format_ident;
4use syn::{GenericParam, Generics, Ident, TypeParamBound};
5
6/// Convert a type to an `ident`.
7pub trait ToIdent {
8    /// Interpret the type as an `ident`.
9    fn as_ident(&self) -> Ident;
10}
11impl<T: AsRef<str>> ToIdent for T {
12    fn as_ident(&self) -> Ident {
13        format_ident!("{}", self.as_ref())
14    }
15}
16
17/// Add a bound to every type parameter `T`.
18pub fn add_trait_bounds(mut generics: Generics, bounds: TypeParamBound) -> Generics {
19    for param in &mut generics.params {
20        if let GenericParam::Type(ref mut type_param) = *param {
21            type_param.bounds.push(bounds.clone());
22        }
23    }
24    generics
25}
26
27/// Extract the inner type from an option.
28///
29/// From <https://stackoverflow.com/a/56264023>
30pub fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> {
31    use syn::{GenericArgument, Path, PathArguments, PathSegment};
32
33    /// Extract the type path.
34    fn extract_type_path(ty: &syn::Type) -> Option<&Path> {
35        match *ty {
36            syn::Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path),
37            _ => None,
38        }
39    }
40
41    /// Extract the option segment.
42    fn extract_option_segment(path: &Path) -> Option<&PathSegment> {
43        let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| {
44            acc.push_str(&v.ident.to_string());
45            acc.push('|');
46            acc
47        });
48        ["Option|", "std|option|Option|", "core|option|Option|"]
49            .into_iter()
50            .find(|s| idents_of_path == *s)
51            .and_then(|_| path.segments.last())
52    }
53
54    extract_type_path(ty)
55        .and_then(|path| extract_option_segment(path))
56        .and_then(|path_seg| {
57            let type_params = &path_seg.arguments;
58            // It should have only on angle-bracketed param ("<String>"):
59            match *type_params {
60                PathArguments::AngleBracketed(ref params) => params.args.first(),
61                _ => None,
62            }
63        })
64        .and_then(|generic_arg| match *generic_arg {
65            GenericArgument::Type(ref ty) => Some(ty),
66            _ => None,
67        })
68}