opendp_tooling/bootstrap/
signature.rs1use std::collections::HashSet;
2
3use darling::{Error, Result};
4use syn::{
5 FnArg, GenericArgument, GenericParam, Pat, Path, PathArguments, ReturnType, Signature, Type,
6 TypeParam, TypePath, TypePtr, TypeReference,
7};
8
9use crate::TypeRecipe;
10
11use super::partial::supports_partial;
12
13pub struct BootstrapSignature {
17 pub name: String,
18 pub arguments: Vec<(String, BootSigArgType)>,
19 pub generics: Vec<String>,
20 pub output_c_type: Result<String>,
21 pub supports_partial: bool,
22}
23
24pub struct BootSigArgType {
25 pub c_type: Result<String>,
26 pub rust_type: Result<TypeRecipe>,
27}
28
29impl BootstrapSignature {
30 pub fn from_syn(sig: Signature) -> Result<Self> {
31 let supports_partial = supports_partial(&sig);
32
33 let generics = sig
34 .generics
35 .params
36 .into_iter()
37 .map(|generic| syn_generic_to_syn_type_param(&generic).map(|v| v.ident.to_string()))
38 .collect::<Result<Vec<_>>>()?;
39
40 Ok(BootstrapSignature {
41 name: sig.ident.to_string(),
42 arguments: sig
43 .inputs
44 .into_iter()
45 .map(|fn_arg| {
46 let (pat, ty) = syn_fnarg_to_syn_pattype(fn_arg)?;
47
48 Ok((
49 syn_pat_to_string(&pat)?,
50 BootSigArgType {
51 rust_type: syn_type_to_type_recipe(&ty),
52 c_type: syn_type_to_c_type(ty, &HashSet::from_iter(generics.clone())),
53 },
54 ))
55 })
56 .collect::<Result<Vec<_>>>()?,
57 generics: generics.clone(),
58 output_c_type: syn_type_to_c_type(
59 match sig.output {
60 ReturnType::Default => {
61 return Err(Error::custom(
62 "default return types are not supported in bootstrap functions",
63 )
64 .with_span(&sig.output))
65 }
66 ReturnType::Type(_, ty) => *ty,
67 },
68 &HashSet::from_iter(generics),
69 ),
70 supports_partial,
71 })
72 }
73}
74
75fn syn_generic_to_syn_type_param(generic: &GenericParam) -> Result<&TypeParam> {
76 match generic {
77 GenericParam::Type(v) => Ok(v),
78 GenericParam::Lifetime(l) => {
79 Err(Error::custom("lifetimes are not supported in bootstrap functions").with_span(l))
80 }
81 GenericParam::Const(c) => {
82 Err(Error::custom("consts are not supported in bootstrap functions").with_span(c))
83 }
84 }
85}
86
87pub(super) fn syn_path_to_string(path: &Path) -> Result<String> {
88 match path.get_ident() {
89 Some(ident) => Ok(ident.to_string()),
90 None => Err(Error::custom("path must be consist of a single identifier").with_span(&path)),
91 }
92}
93
94fn syn_pat_to_string(pat: &Pat) -> Result<String> {
96 match pat {
97 Pat::Box(b) => syn_pat_to_string(&*b.pat),
98 Pat::Ident(i) => Ok(i.ident.to_string()),
99 Pat::Reference(r) => syn_pat_to_string(&*r.pat),
100 Pat::Type(t) => syn_pat_to_string(&*t.pat),
101 token => Err(Error::custom("unrecognized pattern in argument").with_span(&token)),
102 }
103}
104
105pub(super) fn syn_type_to_type_recipe(ty: &Type) -> Result<TypeRecipe> {
106 Ok(match ty {
107 Type::Path(tpath) => {
108 let segment = (tpath.path.segments.last()).ok_or_else(|| {
109 Error::custom("paths must have at least one segment").with_span(ty)
110 })?;
111
112 let name = segment.ident.to_string();
113 match &segment.arguments {
114 PathArguments::None => TypeRecipe::Name(name),
115 PathArguments::AngleBracketed(ab) => {
116 let args = (ab.args.iter())
117 .map(|arg| syn_type_to_type_recipe(syn_generic_arg_to_syn_type(arg)?))
118 .collect::<Result<Vec<_>>>()?;
119
120 TypeRecipe::Nest {
121 origin: name,
122 args: args,
123 }
124 }
125 PathArguments::Parenthesized(p) => {
126 return Err(Error::custom("parenthesized paths are not supported").with_span(p))
127 }
128 }
129 .into()
130 }
131 Type::Reference(refer) => syn_type_to_type_recipe(&*refer.elem)?,
132 Type::Tuple(tuple) => TypeRecipe::Nest {
133 origin: "Tuple".to_string(),
134 args: (tuple.elems.iter())
135 .map(|ty| syn_type_to_type_recipe(ty))
136 .collect::<Result<Vec<_>>>()?,
137 }
138 .into(),
139 Type::Ptr(ptr) => syn_type_to_type_recipe(&*ptr.elem)?,
140 t => return Err(Error::custom("unrecognized type for TypeRecipe").with_span(t)),
141 })
142}
143
144fn syn_generic_arg_to_syn_type(arg: &GenericArgument) -> Result<&Type> {
145 if let GenericArgument::Type(ty) = arg {
146 Ok(ty)
147 } else {
148 Err(Error::custom("generic arguments in this position must be a type").with_span(arg))
149 }
150}
151
152fn syn_type_to_c_type(ty: Type, generics: &HashSet<String>) -> Result<String> {
153 Ok(match ty {
154 Type::Path(TypePath { path, .. }) => {
155 let segment = (path.segments.last())
156 .ok_or_else(|| Error::custom("at least one segment required").with_span(&path))?;
157
158 match segment.ident.to_string() {
159 i if i == "Option" => {
160 let first_arg = if let PathArguments::AngleBracketed(ab) = &segment.arguments {
161 ab.args.first().ok_or_else(|| {
162 Error::custom("Option must have one argument").with_span(&ab)
163 })?
164 } else {
165 return Err(
166 Error::custom("Option must have angle brackets").with_span(segment)
167 );
168 };
169
170 let inner_c_type = if let GenericArgument::Type(ty) = first_arg {
171 syn_type_to_c_type(ty.clone(), generics)?
172 } else {
173 return Err(
174 Error::custom("Option's argument must be a Type").with_span(segment)
175 );
176 };
177 match inner_c_type.as_str() {
178 "AnyObject *" => "AnyObject *".to_string(),
179 "char *" => "char *".to_string(),
180 _ => "void *".to_string(),
181 }
182 }
183 i if i == "String" => "AnyObject *".to_string(),
184 i if i == "str" => "char *".to_string(),
185 i if i == "c_char" => "char *".to_string(),
186 i if i == "AnyObject" => "AnyObject *".to_string(),
187 i if i == "Vec" => "AnyObject *".to_string(),
188 i if i == "HashSet" => "AnyObject *".to_string(),
189 i if i == "bool" || i == "c_bool" => "bool".to_string(),
190 i if i == "i8" => "int8_t".to_string(),
191 i if i == "i16" => "int16_t".to_string(),
192 i if i == "i32" => "int32_t".to_string(),
193 i if i == "i64" => "int64_t".to_string(),
194 i if i == "u8" => "uint8_t".to_string(),
195 i if i == "u16" => "uint16_t".to_string(),
196 i if i == "u32" => "uint32_t".to_string(),
197 i if i == "u64" => "uint64_t".to_string(),
198 i if i == "f32" => "float".to_string(),
199 i if i == "f64" => "double".to_string(),
200 i if i == "usize" => "size_t".to_string(),
201 i if i == "DataFrame" => "AnyObject *".to_string(),
202 i if i == "LazyFrame" => "AnyObject *".to_string(),
203 i if i == "Expr" => "AnyObject *".to_string(),
204 i if i == "LazyFrameDomain" => "AnyDomain *".to_string(),
205 i if i == "FfiSlice" => "FfiSlice *".to_string(),
206 i if i == "Transformation" => "AnyTransformation *".to_string(),
207 i if i == "ExtrinsicObject" => "ExtrinsicObject *".to_string(),
208 i if i == "Measurement" => "AnyMeasurement *".to_string(),
209 i if i == "Function" => "AnyFunction *".to_string(),
210 i if i == "AnyFunction" => "AnyFunction *".to_string(),
211 i if i == "AnyTransformation" => "AnyTransformation *".to_string(),
212 i if i == "AnyMeasurement" => "AnyMeasurement *".to_string(),
213 i if i == "AnyQueryable" => "AnyQueryable *".to_string(),
214 i if i == "AnyDomain" => "AnyDomain *".to_string(),
215 i if i == "AnyMetric" => "AnyMetric *".to_string(),
216 i if i == "AnyMeasure" => "AnyMeasure *".to_string(),
217 i if i == "CallbackFn" => "CallbackFn".to_string(),
218 i if i == "TransitionFn" => "TransitionFn".to_string(),
219 i if i == "Fallible" || i == "FfiResult" => {
220 let args = match &segment.arguments {
221 PathArguments::AngleBracketed(ref ab) => &ab.args,
222 args => {
223 return Err(Error::custom("Fallible expects one type argument")
224 .with_span(&args))
225 }
226 };
227
228 if args.len() != 1 {
229 return Err(Error::custom("Fallible expects one argument"));
230 }
231 let rtype = syn_generic_arg_to_syn_type(&args[0])?;
232 format!(
233 "FfiResult<{}>",
234 syn_type_to_c_type(rtype.clone(), generics)?
235 )
236 }
237 i if generics.contains(&i) => "AnyObject *".to_string(),
238 _ => {
239 return Err(Error::custom(
240 "Unrecognized rust type. Failed to convert to C type.",
241 )
242 .with_span(segment))
243 }
244 }
245 }
246 Type::Tuple(_) => "AnyObject *".to_string(),
247 Type::Reference(TypeReference { elem, .. }) => syn_type_to_c_type(*elem, generics)?,
248 Type::Ptr(TypePtr { elem, .. }) => syn_type_to_c_type(*elem, generics)?,
249 ty => {
250 return Err(Error::custom(
251 "Unrecognized rust type structure. Failed to convert to C type.",
252 )
253 .with_span(&ty))
254 }
255 })
256}
257
258pub fn syn_fnarg_to_syn_pattype(v: FnArg) -> Result<(Pat, Type)> {
259 match v {
260 FnArg::Receiver(r) => {
261 let msg = "bootstrapped functions don't support receiver (self) args";
262 Err(Error::custom(msg).with_span(&r))
263 }
264 FnArg::Typed(t) => Ok((*t.pat, *t.ty)),
265 }
266}