miniextendr_macros/
type_inspect.rs1pub(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
19pub(crate) fn first_type_argument(seg: &syn::PathSegment) -> Option<&syn::Type> {
21 nth_type_argument(seg, 0)
22}
23
24pub(crate) fn second_type_argument(seg: &syn::PathSegment) -> Option<&syn::Type> {
26 nth_type_argument(seg, 1)
27}
28
29#[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
40const MAIN_THREAD_BOUND: &[&str] = &[
51 "SEXP",
52 "AltrepSexp",
53 "RDVector",
54 "RDMatrix",
55 "RndVec",
56 "RndMat",
57 "ProtectedStrVec",
58 "BuiltDataFrame",
59 "DataFrameShape",
60];
61
62#[inline]
69pub(crate) fn is_main_thread_bound_input(ty: &syn::Type) -> bool {
70 matches!(ty, syn::Type::Path(p) if p
71 .path
72 .segments
73 .last()
74 .map(|s| MAIN_THREAD_BOUND.contains(&s.ident.to_string().as_str()))
75 .unwrap_or(false))
76}
77
78pub(crate) fn is_main_thread_bound_return(ty: &syn::Type) -> bool {
88 match ty {
89 syn::Type::Path(p) => p.path.segments.last().is_some_and(|seg| {
90 if MAIN_THREAD_BOUND.contains(&seg.ident.to_string().as_str()) {
91 return true;
92 }
93 if let syn::PathArguments::AngleBracketed(ab) = &seg.arguments {
94 return ab.args.iter().any(|arg| {
95 matches!(arg, syn::GenericArgument::Type(t) if is_main_thread_bound_return(t))
96 });
97 }
98 false
99 }),
100 syn::Type::Reference(r) => is_main_thread_bound_return(&r.elem),
101 syn::Type::Paren(p) => is_main_thread_bound_return(&p.elem),
102 syn::Type::Group(g) => is_main_thread_bound_return(&g.elem),
103 syn::Type::Tuple(t) => t.elems.iter().any(is_main_thread_bound_return),
104 syn::Type::Array(a) => is_main_thread_bound_return(&a.elem),
105 syn::Type::Slice(s) => is_main_thread_bound_return(&s.elem),
106 _ => false,
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::is_main_thread_bound_return;
113
114 fn ty(s: &str) -> syn::Type {
115 syn::parse_str(s).unwrap()
116 }
117
118 #[test]
119 fn main_thread_bound_return_detects_nested_positions() {
120 assert!(is_main_thread_bound_return(&ty("BuiltDataFrame")));
122 assert!(is_main_thread_bound_return(&ty(
123 "miniextendr_api::dataframe::BuiltDataFrame"
124 )));
125 assert!(is_main_thread_bound_return(&ty("DataFrameShape")));
126 assert!(is_main_thread_bound_return(&ty("SEXP")));
127 assert!(is_main_thread_bound_return(&ty(
129 "Result<BuiltDataFrame, String>"
130 )));
131 assert!(is_main_thread_bound_return(&ty(
132 "Result<DataFrameShape, std::string::String>"
133 )));
134 assert!(is_main_thread_bound_return(&ty("Option<BuiltDataFrame>")));
135 assert!(is_main_thread_bound_return(&ty("Vec<BuiltDataFrame>")));
136 assert!(is_main_thread_bound_return(&ty("(i32, BuiltDataFrame)")));
137 assert!(!is_main_thread_bound_return(&ty("i32")));
139 assert!(!is_main_thread_bound_return(&ty(
140 "Result<Vec<f64>, String>"
141 )));
142 assert!(!is_main_thread_bound_return(&ty("ExternalPtr<MyType>")));
143 assert!(!is_main_thread_bound_return(&ty("DataFrame")));
144 }
145}
146
147#[derive(Debug, Clone)]
150pub(crate) enum SeveralOkContainer {
151 Vec,
153 BoxedSlice,
155 Array(usize),
157 BorrowedSlice,
159}
160
161pub(crate) fn classify_several_ok_container(
167 ty: &syn::Type,
168) -> Option<(SeveralOkContainer, &syn::Type)> {
169 match ty {
170 syn::Type::Path(tp) => {
172 let seg = tp.path.segments.last()?;
173 if seg.ident == "Vec" {
174 let inner = first_type_argument(seg)?;
175 return Some((SeveralOkContainer::Vec, inner));
176 }
177 if seg.ident == "Box"
179 && let syn::PathArguments::AngleBracketed(ab) = &seg.arguments
180 {
181 for arg in &ab.args {
182 if let syn::GenericArgument::Type(syn::Type::Slice(s)) = arg {
183 return Some((SeveralOkContainer::BoxedSlice, s.elem.as_ref()));
184 }
185 }
186 }
187 None
188 }
189 syn::Type::Array(arr) => {
191 if let syn::Expr::Lit(syn::ExprLit {
192 lit: syn::Lit::Int(n),
193 ..
194 }) = &arr.len
195 {
196 let n = n.base10_parse::<usize>().ok()?;
197 return Some((SeveralOkContainer::Array(n), arr.elem.as_ref()));
198 }
199 None
200 }
201 syn::Type::Reference(r) => {
203 if let syn::Type::Slice(s) = r.elem.as_ref() {
204 return Some((SeveralOkContainer::BorrowedSlice, s.elem.as_ref()));
205 }
206 None
207 }
208 _ => None,
209 }
210}