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