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