bon_macros/util/ty/
mod.rs

1mod match_types;
2
3use crate::util::prelude::*;
4use syn::punctuated::Punctuated;
5
6pub(crate) trait TypeExt {
7    /// Try downcasting the type to [`syn::Type::Path`]
8    fn as_path(&self) -> Option<&syn::TypePath>;
9
10    /// Try downcasting the type to [`syn::Type::Path`]. If it has a [`syn::QSelf`]
11    /// then this method will return `None`.
12    fn as_path_no_qself(&self) -> Option<&syn::Path>;
13
14    /// Detects if the type is [`Option`] and returns its generic type parameter
15    fn option_type_param(&self) -> Option<&syn::Type>;
16
17    /// Validates that this type is a generic type (path without [`syn::QSelf`])
18    /// which ends with the given last segment that passes the predicate
19    /// `is_desired_last_segment`.
20    fn as_generic_angle_bracketed_path(
21        &self,
22        is_desired_last_segment: impl FnOnce(&syn::Ident) -> bool,
23    ) -> Option<GenericAngleBracketedPath<'_>>;
24
25    /// Heuristically detects if the type is [`Option`]
26    fn is_option(&self) -> bool;
27
28    /// Recursively strips the [`syn::Type::Group`] and [`syn::Type::Paren`] wrappers
29    fn peel(&self) -> &Self;
30
31    /// Returns `true` if the given type matches the pattern. The types match only if
32    /// their tokens are equal or if they differ in the places where the pattern has
33    /// a wildcard [`syn::Type::Infer`] e.g. `Vec<i32>` matches the pattern `Vec<_>`.
34    ///
35    /// Any wildcards in `Self` will not be specially handled. Only wildcards in `pattern`
36    /// have semantic meaning.
37    fn matches(&self, pattern: &syn::Type) -> Result<bool>;
38}
39
40impl TypeExt for syn::Type {
41    fn as_path(&self) -> Option<&syn::TypePath> {
42        match self.peel() {
43            Self::Path(path) => Some(path),
44            _ => None,
45        }
46    }
47
48    fn as_path_no_qself(&self) -> Option<&syn::Path> {
49        let path = self.as_path()?;
50        if path.qself.is_some() {
51            return None;
52        }
53        Some(&path.path)
54    }
55
56    fn option_type_param(&self) -> Option<&syn::Type> {
57        let ty = self.as_generic_angle_bracketed_path(|last_segment| last_segment == "Option")?;
58
59        if ty.args.len() != 1 {
60            return None;
61        }
62
63        let arg = ty.args.first()?;
64
65        let arg = match arg {
66            syn::GenericArgument::Type(arg) => arg,
67            _ => return None,
68        };
69
70        Some(arg)
71    }
72
73    fn as_generic_angle_bracketed_path(
74        &self,
75        is_desired_last_segment: impl FnOnce(&syn::Ident) -> bool,
76    ) -> Option<GenericAngleBracketedPath<'_>> {
77        let path = self.as_path_no_qself()?;
78
79        let last_segment = path.segments.last()?;
80
81        if !is_desired_last_segment(&last_segment.ident) {
82            return None;
83        }
84
85        let args = match &last_segment.arguments {
86            syn::PathArguments::AngleBracketed(args) => &args.args,
87            _ => return None,
88        };
89
90        Some(GenericAngleBracketedPath { path, args })
91    }
92
93    fn is_option(&self) -> bool {
94        self.option_type_param().is_some()
95    }
96
97    fn peel(&self) -> &Self {
98        match self {
99            Self::Group(group) => group.elem.peel(),
100            Self::Paren(paren) => paren.elem.peel(),
101            _ => self,
102        }
103    }
104
105    fn matches(&self, pattern: &syn::Type) -> Result<bool> {
106        match_types::match_types(self, pattern)
107    }
108}
109
110pub(crate) struct GenericAngleBracketedPath<'a> {
111    pub(crate) path: &'a syn::Path,
112    pub(crate) args: &'a Punctuated<syn::GenericArgument, syn::Token![,]>,
113}