1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(clippy::all)]
5#![allow(unused_unsafe)]
6#![allow(unused_variables)]
7#![doc = include_str!("../README.md")]
8
9mod common;
10mod generator;
11mod parser;
12
13pub use common::*;
14pub use generator::*;
15pub use parser::*;
16
17use proc_macro2::TokenStream;
18use std::fs::OpenOptions;
19use std::io::Write;
20use std::process::{Command, Stdio};
21
22pub const CUSTOM_AERON_CODE: &str = include_str!("./aeron_custom.rs");
23pub const COMMON_CODE: &str = include_str!("./common.rs");
24
25pub fn append_to_file(file_path: &str, code: &str) -> std::io::Result<()> {
26 let mut file = OpenOptions::new()
28 .create(true)
29 .write(true)
30 .append(true)
31 .open(file_path)?;
32
33 writeln!(file, "\n{}", code)?;
35
36 Ok(())
37}
38
39#[allow(dead_code)]
40pub fn format_with_rustfmt(code: &str) -> Result<String, std::io::Error> {
41 let mut rustfmt = Command::new("rustfmt")
42 .stdin(Stdio::piped())
43 .stdout(Stdio::piped())
44 .spawn()?;
45
46 if let Some(mut stdin) = rustfmt.stdin.take() {
47 stdin.write_all(code.as_bytes())?;
48 }
49
50 let output = rustfmt.wait_with_output()?;
51 let formatted_code = String::from_utf8_lossy(&output.stdout).to_string();
52
53 Ok(formatted_code)
54}
55
56#[allow(dead_code)]
57pub fn format_token_stream(tokens: TokenStream) -> String {
58 let code = tokens.to_string();
59
60 match format_with_rustfmt(&code) {
61 Ok(formatted_code) if !formatted_code.trim().is_empty() => formatted_code,
62 _ => code.replace("{", "{\n"), }
64}
65
66#[cfg(test)]
67mod tests {
68 use crate::generator::MEDIA_DRIVER_BINDINGS;
69 use crate::parser::parse_bindings;
70 use crate::{
71 append_to_file, format_token_stream, format_with_rustfmt, ARCHIVE_BINDINGS,
72 CLIENT_BINDINGS, CUSTOM_AERON_CODE,
73 };
74 use proc_macro2::TokenStream;
75 use std::fs;
76
77 #[test]
78 #[cfg(not(target_os = "windows"))] fn client() {
80 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/client.rs".into());
81 assert_eq!(
82 "AeronImageFragmentAssembler",
83 bindings
84 .wrappers
85 .get("aeron_image_fragment_assembler_t")
86 .unwrap()
87 .class_name
88 );
89 assert_eq!(
90 0,
91 bindings.methods.len(),
92 "expected all methods to have been matched {:#?}",
93 bindings.methods
94 );
95
96 let file = write_to_file(TokenStream::new(), true, "client.rs");
97 let bindings_copy = bindings.clone();
98 for handler in bindings.handlers.iter_mut() {
99 let _ = crate::generate_handlers(handler, &bindings_copy);
101 }
102 for (p, w) in bindings.wrappers.values().enumerate() {
103 let code = crate::generate_rust_code(
104 w,
105 &bindings.wrappers,
106 p == 0,
107 true,
108 true,
109 &bindings.handlers,
110 );
111 write_to_file(code, false, "client.rs");
112 }
113 let bindings_copy = bindings.clone();
114 for handler in bindings.handlers.iter_mut() {
115 let code = crate::generate_handlers(handler, &bindings_copy);
116 append_to_file(&file, &format_with_rustfmt(&code.to_string()).unwrap()).unwrap();
117 }
118
119 let t = trybuild::TestCases::new();
120 append_to_file(&file, "use bindings::*; mod bindings { ").unwrap();
121 append_to_file(&file, CLIENT_BINDINGS).unwrap();
122 append_to_file(&file, "}").unwrap();
123 append_to_file(&file, CUSTOM_AERON_CODE).unwrap();
124 append_to_file(&file, "\npub fn main() {}\n").unwrap();
125 t.pass(file)
126 }
127
128 #[test]
129 #[cfg(not(target_os = "windows"))] fn media_driver() {
131 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/media-driver.rs".into());
132 assert_eq!(
133 "AeronImageFragmentAssembler",
134 bindings
135 .wrappers
136 .get("aeron_image_fragment_assembler_t")
137 .unwrap()
138 .class_name
139 );
140
141 let file = write_to_file(TokenStream::new(), true, "md.rs");
142
143 let bindings_copy = bindings.clone();
144 for handler in bindings.handlers.iter_mut() {
145 let _ = crate::generate_handlers(handler, &bindings_copy);
147 }
148 for (p, w) in bindings
149 .wrappers
150 .values()
151 .filter(|w| !w.type_name.contains("_t_") && w.type_name != "in_addr")
152 .enumerate()
153 {
154 let code = crate::generate_rust_code(
155 w,
156 &bindings.wrappers,
157 p == 0,
158 true,
159 true,
160 &bindings.handlers,
161 );
162 write_to_file(code, false, "md.rs");
163 }
164 let bindings_copy = bindings.clone();
165 for handler in bindings.handlers.iter_mut() {
166 let code = crate::generate_handlers(handler, &bindings_copy);
167 append_to_file(&file, &format_with_rustfmt(&code.to_string()).unwrap()).unwrap();
168 }
169 let t = trybuild::TestCases::new();
170 append_to_file(&file, "use bindings::*; mod bindings { ").unwrap();
171 append_to_file(&file, MEDIA_DRIVER_BINDINGS).unwrap();
172 append_to_file(&file, "}").unwrap();
173 append_to_file(&file, CUSTOM_AERON_CODE).unwrap();
174 append_to_file(&file, "\npub fn main() {}\n").unwrap();
175 t.pass(&file)
176 }
177
178 #[test]
179 #[cfg(not(target_os = "windows"))] fn archive() {
181 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/archive.rs".into());
182 assert_eq!(
183 "AeronImageFragmentAssembler",
184 bindings
185 .wrappers
186 .get("aeron_image_fragment_assembler_t")
187 .unwrap()
188 .class_name
189 );
190
191 let file = write_to_file(TokenStream::new(), true, "archive.rs");
192 let bindings_copy = bindings.clone();
193 for handler in bindings.handlers.iter_mut() {
194 let _ = crate::generate_handlers(handler, &bindings_copy);
196 }
197 for (p, w) in bindings.wrappers.values().enumerate() {
198 let code = crate::generate_rust_code(
199 w,
200 &bindings.wrappers,
201 p == 0,
202 true,
203 true,
204 &bindings.handlers,
205 );
206 write_to_file(code, false, "archive.rs");
207 }
208 let bindings_copy = bindings.clone();
209 for handler in bindings.handlers.iter_mut() {
210 let code = crate::generate_handlers(handler, &bindings_copy);
211 append_to_file(&file, &format_with_rustfmt(&code.to_string()).unwrap()).unwrap();
212 }
213 let t = trybuild::TestCases::new();
214 append_to_file(&file, "use bindings::*; mod bindings { ").unwrap();
215 append_to_file(&file, ARCHIVE_BINDINGS).unwrap();
216 append_to_file(&file, "}").unwrap();
217 append_to_file(&file, CUSTOM_AERON_CODE).unwrap();
218 append_to_file(&file, "\npub fn main() {}\n").unwrap();
219 t.pass(file)
220 }
221
222 fn write_to_file(rust_code: TokenStream, delete: bool, name: &str) -> String {
223 let src = format_token_stream(rust_code);
224 let path = format!("../target/{name}");
225 let path = &path;
226 if delete {
227 let _ = fs::remove_file(path);
228 }
229 append_to_file(path, &src).unwrap();
230 path.to_string()
231 }
232}
233
234#[cfg(test)]
235mod test {
236 use crate::ManagedCResource;
237 use std::sync::atomic::{AtomicBool, Ordering};
238 use std::sync::Arc;
239
240 fn make_resource(val: i32) -> *mut i32 {
241 Box::into_raw(Box::new(val))
242 }
243
244 #[test]
245 fn test_drop_calls_cleanup_non_borrowed_no_cleanup_struct() {
246 let flag = Arc::new(AtomicBool::new(false));
247 let flag_clone = flag.clone();
248 let resource_ptr = make_resource(10);
249
250 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
251 flag_clone.store(true, Ordering::SeqCst);
252 unsafe {
254 *res = std::ptr::null_mut();
255 }
256 0
257 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
258
259 {
260 let _resource = ManagedCResource::new(
261 |res: *mut *mut i32| {
262 unsafe {
263 *res = resource_ptr;
264 }
265 0
266 },
267 cleanup,
268 false,
269 None,
270 )
271 .unwrap();
272 }
273 assert!(flag.load(Ordering::SeqCst));
274 }
275
276 #[test]
277 fn test_drop_calls_cleanup_non_borrowed_with_cleanup_struct() {
278 let flag = Arc::new(AtomicBool::new(false));
279 let flag_clone = flag.clone();
280 let resource_ptr = make_resource(20);
281
282 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
283 flag_clone.store(true, Ordering::SeqCst);
284 unsafe {
285 *res = std::ptr::null_mut();
286 }
287 0
288 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
289
290 {
291 let _resource = ManagedCResource::new(
292 |res: *mut *mut i32| {
293 unsafe {
294 *res = resource_ptr;
295 }
296 0
297 },
298 cleanup,
299 true,
300 None,
301 )
302 .unwrap();
303 }
304 assert!(flag.load(Ordering::SeqCst));
305 }
306
307 #[test]
308 fn test_drop_does_not_call_cleanup_if_already_closed() {
309 let flag = Arc::new(AtomicBool::new(false));
310 let flag_clone = flag.clone();
311 let resource_ptr = make_resource(30);
312
313 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
314 flag_clone.store(true, Ordering::SeqCst);
315 unsafe {
316 *res = std::ptr::null_mut();
317 }
318 0
319 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
320
321 let mut resource = ManagedCResource::new(
322 |res: *mut *mut i32| {
323 unsafe {
324 *res = resource_ptr;
325 }
326 0
327 },
328 cleanup,
329 false,
330 None,
331 )
332 .unwrap();
333
334 resource.close().unwrap();
335 flag.store(false, Ordering::SeqCst);
337 drop(resource);
338 assert!(!flag.load(Ordering::SeqCst));
339 }
340
341 #[test]
342 fn test_drop_does_not_call_cleanup_if_check_for_is_closed_returns_true() {
343 let flag = Arc::new(AtomicBool::new(false));
344 let flag_clone = flag.clone();
345 let resource_ptr = make_resource(60);
346
347 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
348 flag_clone.store(true, Ordering::SeqCst);
349 unsafe {
350 *res = std::ptr::null_mut();
351 }
352 0
353 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
354
355 let check_fn = Some(|_res: *mut i32| -> bool { true } as fn(_) -> bool);
356
357 {
358 let _resource = ManagedCResource::new(
359 |res: *mut *mut i32| {
360 unsafe {
361 *res = resource_ptr;
362 }
363 0
364 },
365 cleanup,
366 false,
367 check_fn,
368 )
369 .unwrap();
370 }
371 assert!(!flag.load(Ordering::SeqCst));
372 }
373
374 #[test]
375 fn test_drop_does_call_cleanup_if_check_for_is_closed_returns_false() {
376 let flag = Arc::new(AtomicBool::new(false));
377 let flag_clone = flag.clone();
378 let resource_ptr = make_resource(60);
379
380 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
381 flag_clone.store(true, Ordering::SeqCst);
382 unsafe {
383 *res = std::ptr::null_mut();
384 }
385 0
386 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
387
388 let check_fn = Some(|_res: *mut i32| -> bool { false } as fn(*mut i32) -> bool);
389
390 {
391 let _resource = ManagedCResource::new(
392 |res: *mut *mut i32| {
393 unsafe {
394 *res = resource_ptr;
395 }
396 0
397 },
398 cleanup,
399 false,
400 check_fn,
401 )
402 .unwrap();
403 }
404 assert!(flag.load(Ordering::SeqCst));
405 }
406}