1use proc_macro2::TokenStream;
2use syn::Ident;
3
4use crate::util::*;
5
6#[derive(Clone, Debug, PartialEq)]
11pub enum FfiType {
12 U8,
13 U16,
14 U32,
15 U64,
16 Usize,
17 I8,
18 I16,
19 I32,
20 I64,
21 Isize,
22 F32,
23 F64,
24 Void,
25 Bool,
26 Pointer,
27 Buffer, FnPointer,
30 }
32
33#[derive(Clone, Debug, PartialEq)]
35pub struct FfiFunction {
36 pub ident: Ident,
37 pub inputs: Vec<FfiType>,
38 pub output: FfiType,
39 pub non_blocking: bool, }
42
43#[derive(Clone, Debug, PartialEq)]
45pub enum FfiSymbol {
46 Function(FfiFunction),
47 }
49
50#[derive(Clone, Debug, Default, PartialEq)]
51pub struct FfiInterface {
52 pub symbols: Vec<FfiSymbol>,
53}
54
55impl FfiInterface {
56 pub fn push_fn(&mut self, ffi_function: FfiFunction) {
57 self.symbols.push(FfiSymbol::Function(ffi_function));
58 }
59 pub fn append(&mut self, other: &mut FfiInterface) {
60 self.symbols.append(&mut other.symbols);
61 }
62}
63
64#[derive(Clone, Debug, Default, PartialEq)]
65pub struct FfiLib {
66 pub interface: FfiInterface,
67 pub dylib_path: String,
68 pub lazy: bool,
69 pub export: bool,
70}
71
72#[rustfmt::skip]
75impl Parse for FfiType {
76 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
77 let lit_str = input.parse::<LitStr>()?;
78 let ffi_type = match lit_str.value().as_str() {
79 "u8" => FfiType::U8,
80 "u16" => FfiType::U16,
81 "u32" => FfiType::U32,
82 "u64" => FfiType::U64,
83 "usize" => FfiType::Usize,
84 "i8" => FfiType::I8,
85 "i16" => FfiType::I16,
86 "i32" => FfiType::I32,
87 "i64" => FfiType::I64,
88 "isize" => FfiType::Isize,
89 "f32" => FfiType::F32,
90 "f64" => FfiType::F64,
91 "void" => FfiType::Void,
92 "bool" => FfiType::Bool,
93 "pointer" => FfiType::Pointer,
94 "buffer" => FfiType::Buffer,
95 "function" => FfiType::FnPointer,
96 _ => return Err(syn::Error::new(lit_str.span(), "unknown ffi type"))
97 };
98 Ok(ffi_type)
99 }
100}
101
102impl Parse for FfiFunction {
103 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
104 let ident = input.parse()?;
106 input.parse::<Token![:]>()?;
107
108 let content;
110 braced!(content in input);
111
112 let _ident = content.call(<Ident as syn::ext::IdentExt>::parse_any)?;
114 if _ident.to_string().as_str() != "parameters" {
115 return Err(syn::Error::new(_ident.span(), "expected key `parameters`"));
116 }
117 content.parse::<Token![:]>()?;
118
119 let _content;
121 bracketed!(_content in content);
122 let mut inputs = Vec::new();
123 while !_content.is_empty() {
124 inputs.push(_content.parse()?);
125 if _content.is_empty() {
126 break;
127 }
128 _content.parse::<Token![,]>()?;
129 }
130 content.parse::<Token![,]>()?;
132
133 let _ident = content.call(<Ident as syn::ext::IdentExt>::parse_any)?;
134 if _ident.to_string().as_str() != "result" {
135 return Err(syn::Error::new(_ident.span(), "expected key `result`"));
136 }
137 content.parse::<Token![:]>()?;
138 let output = content.parse()?;
139 content.parse::<Option<Token![,]>>()?;
140
141 Ok(Self {
142 ident,
143 inputs,
144 output,
145 non_blocking: false,
146 })
147 }
148}
149
150impl Parse for FfiInterface {
151 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
152 let mut symbols = Vec::new();
153
154 while !input.is_empty() {
155 symbols.push(FfiSymbol::Function(input.parse::<FfiFunction>()?));
156 if input.is_empty() {
157 break;
158 }
159 input.parse::<Token![,]>()?;
160 }
161
162 Ok(Self { symbols })
163 }
164}
165
166#[rustfmt::skip]
171impl ToTokens for FfiType {
172 fn to_tokens(&self, tokens: &mut TokenStream) {
173 tokens.extend(match self {
174 FfiType::U8 => quote! { "u8" },
175 FfiType::U16 => quote! { "u16" },
176 FfiType::U32 => quote! { "u32" },
177 FfiType::U64 => quote! { "u64" },
178 FfiType::Usize => quote! { "usize" },
179 FfiType::I8 => quote! { "i8" },
180 FfiType::I16 => quote! { "i16" },
181 FfiType::I32 => quote! { "i32" },
182 FfiType::I64 => quote! { "i64" },
183 FfiType::Isize => quote! { "isize" },
184 FfiType::F32 => quote! { "f32" },
185 FfiType::F64 => quote! { "f64" },
186 FfiType::Void => quote! { "void" },
187 FfiType::Bool => quote! { "bool" },
188 FfiType::Pointer => quote! { "pointer" },
189 FfiType::Buffer => quote! { "buffer" },
190 FfiType::FnPointer => quote! { "function" },
191 });
192 }
193}
194
195impl ToTokens for FfiFunction {
196 fn to_tokens(&self, tokens: &mut TokenStream) {
197 let FfiFunction {
198 ident,
199 inputs,
200 output,
201 non_blocking,
202 } = self;
203
204 let inputs: Vec<&FfiType> = inputs
205 .iter()
206 .filter(|ffi_type| {
207 if **ffi_type == FfiType::Void {
208 false
209 } else {
210 true
211 }
212 })
213 .collect();
214
215 let inputs = quote! { #(#inputs),* };
216 let non_blocking = if *non_blocking {
217 quote! { nonblocking: true }
218 } else {
219 TokenStream::new()
220 };
221
222 tokens.extend(quote! {
223 #ident: {
224 parameters: [#inputs],
225 result: #output,
226 #non_blocking
227 }
228 });
229 }
230}
231
232impl ToTokens for FfiSymbol {
233 fn to_tokens(&self, tokens: &mut TokenStream) {
234 tokens.extend(match self {
235 FfiSymbol::Function(ffi_function) => ffi_function.to_token_stream(),
236 });
237 }
238}
239
240impl ToTokens for FfiInterface {
241 fn to_tokens(&self, tokens: &mut TokenStream) {
242 let symbols = &self.symbols;
243 tokens.extend(quote! {
244 { #(#symbols),* }
245 });
246 }
247}
248
249impl<'a> ToTokens for FfiLib {
250 fn to_tokens(&self, tokens: &mut TokenStream) {
251 let FfiLib {
252 interface,
253 dylib_path,
254 lazy,
255 export,
256 } = self;
257
258 let export = if *export {
259 quote! { export }
260 } else {
261 TokenStream::new()
262 };
263
264 tokens.extend(if *lazy {
265 quote! {
267 let symbols: any;
268 export function load(path: string = #dylib_path) {
269 const { dlopen } = Deno;
270 const { symbols: symbols_ } = dlopen(path, #interface);
271 symbols = symbols_;
272 };
273 }
274 } else {
275 quote! {
276 #export const { symbols } = Deno.dlopen(#dylib_path, #interface);
277 }
278 });
279 }
280}
281
282#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn test_print() {
292 let ffi_function = FfiFunction {
293 ident: format_ident!("some_symbol"),
294 inputs: vec![FfiType::Pointer, FfiType::U8],
295 output: FfiType::Void,
296 non_blocking: false,
297 };
298 println!("{}", ffi_function.to_token_stream().to_string());
299 }
300}