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, RB,
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 rb() {
181 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/rb.rs".into());
182
183 let file = write_to_file(TokenStream::new(), true, "rb.rs");
184
185 let bindings_copy = bindings.clone();
186 for handler in bindings.handlers.iter_mut() {
187 let _ = crate::generate_handlers(handler, &bindings_copy);
189 }
190
191 for (p, w) in bindings.wrappers.values().enumerate() {
192 let code = crate::generate_rust_code(
193 w,
194 &bindings.wrappers,
195 p == 0,
196 true,
197 false,
198 &bindings.handlers,
199 );
200 if code.to_string().contains("ndler : Option < AeronCloseClientHandlerImpl > , rbd :) -> Result < Self , AeronCError > { let resource = Manage") {
201 panic!("{}", format_token_stream(code));
202 }
203
204 write_to_file(code, false, "rb.rs");
205 }
206
207 let bindings_copy = bindings.clone();
208 for handler in bindings.handlers.iter_mut() {
209 let code = crate::generate_handlers(handler, &bindings_copy);
210 append_to_file(&file, &format_with_rustfmt(&code.to_string()).unwrap()).unwrap();
211 }
212
213 let t = trybuild::TestCases::new();
214 append_to_file(&file, RB).unwrap();
215 append_to_file(&file, "\npub fn main() {}\n").unwrap();
216 t.pass(file)
217 }
218
219 #[test]
220 #[cfg(not(target_os = "windows"))] fn archive() {
222 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/archive.rs".into());
223 assert_eq!(
224 "AeronImageFragmentAssembler",
225 bindings
226 .wrappers
227 .get("aeron_image_fragment_assembler_t")
228 .unwrap()
229 .class_name
230 );
231
232 let file = write_to_file(TokenStream::new(), true, "archive.rs");
233 let bindings_copy = bindings.clone();
234 for handler in bindings.handlers.iter_mut() {
235 let _ = crate::generate_handlers(handler, &bindings_copy);
237 }
238 for (p, w) in bindings.wrappers.values().enumerate() {
239 let code = crate::generate_rust_code(
240 w,
241 &bindings.wrappers,
242 p == 0,
243 true,
244 true,
245 &bindings.handlers,
246 );
247 write_to_file(code, false, "archive.rs");
248 }
249 let bindings_copy = bindings.clone();
250 for handler in bindings.handlers.iter_mut() {
251 let code = crate::generate_handlers(handler, &bindings_copy);
252 append_to_file(&file, &format_with_rustfmt(&code.to_string()).unwrap()).unwrap();
253 }
254 let t = trybuild::TestCases::new();
255 append_to_file(&file, "use bindings::*; mod bindings { ").unwrap();
256 append_to_file(&file, ARCHIVE_BINDINGS).unwrap();
257 append_to_file(&file, "}").unwrap();
258 append_to_file(&file, CUSTOM_AERON_CODE).unwrap();
259 append_to_file(&file, "\npub fn main() {}\n").unwrap();
260 t.pass(file)
261 }
262
263 fn write_to_file(rust_code: TokenStream, delete: bool, name: &str) -> String {
264 let src = format_token_stream(rust_code);
265 let path = format!("../target/{name}");
266 let path = &path;
267 if delete {
268 let _ = fs::remove_file(path);
269 }
270 append_to_file(path, &src).unwrap();
271 path.to_string()
272 }
273}
274
275#[cfg(test)]
276mod test {
277 use crate::ManagedCResource;
278 use std::sync::atomic::{AtomicBool, Ordering};
279 use std::sync::Arc;
280
281 fn make_resource(val: i32) -> *mut i32 {
282 Box::into_raw(Box::new(val))
283 }
284
285 #[test]
286 fn test_drop_calls_cleanup_non_borrowed_no_cleanup_struct() {
287 let flag = Arc::new(AtomicBool::new(false));
288 let flag_clone = flag.clone();
289 let resource_ptr = make_resource(10);
290
291 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
292 flag_clone.store(true, Ordering::SeqCst);
293 unsafe {
295 *res = std::ptr::null_mut();
296 }
297 0
298 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
299
300 {
301 let _resource = ManagedCResource::new(
302 |res: *mut *mut i32| {
303 unsafe {
304 *res = resource_ptr;
305 }
306 0
307 },
308 cleanup,
309 false,
310 None,
311 )
312 .unwrap();
313 }
314 assert!(flag.load(Ordering::SeqCst));
315 }
316
317 #[test]
318 fn test_drop_calls_cleanup_non_borrowed_with_cleanup_struct() {
319 let flag = Arc::new(AtomicBool::new(false));
320 let flag_clone = flag.clone();
321 let resource_ptr = make_resource(20);
322
323 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
324 flag_clone.store(true, Ordering::SeqCst);
325 unsafe {
326 *res = std::ptr::null_mut();
327 }
328 0
329 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
330
331 {
332 let _resource = ManagedCResource::new(
333 |res: *mut *mut i32| {
334 unsafe {
335 *res = resource_ptr;
336 }
337 0
338 },
339 cleanup,
340 true,
341 None,
342 )
343 .unwrap();
344 }
345 assert!(flag.load(Ordering::SeqCst));
346 }
347
348 #[test]
349 fn test_drop_does_not_call_cleanup_if_already_closed() {
350 let flag = Arc::new(AtomicBool::new(false));
351 let flag_clone = flag.clone();
352 let resource_ptr = make_resource(30);
353
354 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
355 flag_clone.store(true, Ordering::SeqCst);
356 unsafe {
357 *res = std::ptr::null_mut();
358 }
359 0
360 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
361
362 let mut resource = ManagedCResource::new(
363 |res: *mut *mut i32| {
364 unsafe {
365 *res = resource_ptr;
366 }
367 0
368 },
369 cleanup,
370 false,
371 None,
372 )
373 .unwrap();
374
375 resource.close().unwrap();
376 flag.store(false, Ordering::SeqCst);
378 drop(resource);
379 assert!(!flag.load(Ordering::SeqCst));
380 }
381
382 #[test]
383 fn test_drop_does_not_call_cleanup_if_check_for_is_closed_returns_true() {
384 let flag = Arc::new(AtomicBool::new(false));
385 let flag_clone = flag.clone();
386 let resource_ptr = make_resource(60);
387
388 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
389 flag_clone.store(true, Ordering::SeqCst);
390 unsafe {
391 *res = std::ptr::null_mut();
392 }
393 0
394 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
395
396 let check_fn = Some(|_res: *mut i32| -> bool { true } as fn(_) -> bool);
397
398 {
399 let _resource = ManagedCResource::new(
400 |res: *mut *mut i32| {
401 unsafe {
402 *res = resource_ptr;
403 }
404 0
405 },
406 cleanup,
407 false,
408 check_fn,
409 )
410 .unwrap();
411 }
412 assert!(!flag.load(Ordering::SeqCst));
413 }
414
415 #[test]
416 fn test_drop_does_call_cleanup_if_check_for_is_closed_returns_false() {
417 let flag = Arc::new(AtomicBool::new(false));
418 let flag_clone = flag.clone();
419 let resource_ptr = make_resource(60);
420
421 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
422 flag_clone.store(true, Ordering::SeqCst);
423 unsafe {
424 *res = std::ptr::null_mut();
425 }
426 0
427 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
428
429 let check_fn = Some(|_res: *mut i32| -> bool { false } as fn(*mut i32) -> bool);
430
431 {
432 let _resource = ManagedCResource::new(
433 |res: *mut *mut i32| {
434 unsafe {
435 *res = resource_ptr;
436 }
437 0
438 },
439 cleanup,
440 false,
441 check_fn,
442 )
443 .unwrap();
444 }
445 assert!(flag.load(Ordering::SeqCst));
446 }
447}