Skip to main content

miniextendr_macros/
type_inspect.rs

1//! Lightweight type-introspection helpers shared by parsing and codegen.
2
3/// Returns the `n`-th generic type argument from a path segment.
4pub(crate) fn nth_type_argument(seg: &syn::PathSegment, n: usize) -> Option<&syn::Type> {
5    if let syn::PathArguments::AngleBracketed(ab) = &seg.arguments {
6        let mut count = 0;
7        for arg in ab.args.iter() {
8            if let syn::GenericArgument::Type(ty) = arg {
9                if count == n {
10                    return Some(ty);
11                }
12                count += 1;
13            }
14        }
15    }
16    None
17}
18
19/// Returns the first generic type argument from a path segment.
20pub(crate) fn first_type_argument(seg: &syn::PathSegment) -> Option<&syn::Type> {
21    nth_type_argument(seg, 0)
22}
23
24/// Returns the second generic type argument from a path segment.
25pub(crate) fn second_type_argument(seg: &syn::PathSegment) -> Option<&syn::Type> {
26    nth_type_argument(seg, 1)
27}
28
29/// Returns `true` if `ty` is syntactically `SEXP`.
30#[inline]
31pub(crate) fn is_sexp_type(ty: &syn::Type) -> bool {
32    matches!(ty, syn::Type::Path(p) if p
33        .path
34        .segments
35        .last()
36        .map(|s| s.ident == "SEXP")
37        .unwrap_or(false))
38}
39
40/// Container family for a `several_ok` parameter, returned by
41/// [`classify_several_ok_container`].
42#[derive(Debug, Clone)]
43pub(crate) enum SeveralOkContainer {
44    /// `Vec<T>`
45    Vec,
46    /// `Box<[T]>`
47    BoxedSlice,
48    /// `[T; N]` — the `usize` is the fixed array length N
49    Array(usize),
50    /// `&[T]` or `&mut [T]` — allocate `Vec<T>` then borrow
51    BorrowedSlice,
52}
53
54/// Classify a `several_ok` parameter type into one of the four container
55/// families and extract its inner element type `T`.
56///
57/// Returns `Some((container, inner_ty))` or `None` if the type is not one of
58/// the four accepted container shapes.
59pub(crate) fn classify_several_ok_container(
60    ty: &syn::Type,
61) -> Option<(SeveralOkContainer, &syn::Type)> {
62    match ty {
63        // Vec<T>
64        syn::Type::Path(tp) => {
65            let seg = tp.path.segments.last()?;
66            if seg.ident == "Vec" {
67                let inner = first_type_argument(seg)?;
68                return Some((SeveralOkContainer::Vec, inner));
69            }
70            // Box<[T]>
71            if seg.ident == "Box"
72                && let syn::PathArguments::AngleBracketed(ab) = &seg.arguments
73            {
74                for arg in &ab.args {
75                    if let syn::GenericArgument::Type(syn::Type::Slice(s)) = arg {
76                        return Some((SeveralOkContainer::BoxedSlice, s.elem.as_ref()));
77                    }
78                }
79            }
80            None
81        }
82        // [T; N]
83        syn::Type::Array(arr) => {
84            if let syn::Expr::Lit(syn::ExprLit {
85                lit: syn::Lit::Int(n),
86                ..
87            }) = &arr.len
88            {
89                let n = n.base10_parse::<usize>().ok()?;
90                return Some((SeveralOkContainer::Array(n), arr.elem.as_ref()));
91            }
92            None
93        }
94        // &[T] or &mut [T]
95        syn::Type::Reference(r) => {
96            if let syn::Type::Slice(s) = r.elem.as_ref() {
97                return Some((SeveralOkContainer::BorrowedSlice, s.elem.as_ref()));
98            }
99            None
100        }
101        _ => None,
102    }
103}