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
238 use std::sync::atomic::{AtomicBool, Ordering};
239 use std::sync::Arc;
240
241 fn make_resource(val: i32) -> *mut i32 {
242 Box::into_raw(Box::new(val))
243 }
244
245 #[test]
246 fn test_drop_calls_cleanup_non_borrowed_no_cleanup_struct() {
247 let flag = Arc::new(AtomicBool::new(false));
248 let flag_clone = flag.clone();
249 let resource_ptr = make_resource(10);
250
251 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
252 flag_clone.store(true, Ordering::SeqCst);
253 unsafe {
255 *res = std::ptr::null_mut();
256 }
257 0
258 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
259
260 {
261 let _resource = ManagedCResource::new(
262 |res: *mut *mut i32| {
263 unsafe {
264 *res = resource_ptr;
265 }
266 0
267 },
268 cleanup,
269 false,
270 None,
271 );
272 assert!(_resource.is_ok())
273 }
274 assert!(flag.load(Ordering::SeqCst));
275 }
276
277 #[test]
278 fn test_drop_calls_cleanup_non_borrowed_with_cleanup_struct() {
279 let flag = Arc::new(AtomicBool::new(false));
280 let flag_clone = flag.clone();
281 let resource_ptr = make_resource(20);
282
283 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
284 flag_clone.store(true, Ordering::SeqCst);
285 unsafe {
286 *res = std::ptr::null_mut();
287 }
288 0
289 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
290
291 {
292 let _resource = ManagedCResource::new(
293 |res: *mut *mut i32| {
294 unsafe {
295 *res = resource_ptr;
296 }
297 0
298 },
299 cleanup,
300 true,
301 None,
302 );
303 assert!(_resource.is_ok())
304 }
305 assert!(flag.load(Ordering::SeqCst));
306 }
307
308 #[test]
309 fn test_drop_does_not_call_cleanup_if_already_closed() {
310 let flag = Arc::new(AtomicBool::new(false));
311 let flag_clone = flag.clone();
312 let resource_ptr = make_resource(30);
313
314 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
315 flag_clone.store(true, Ordering::SeqCst);
316 unsafe {
317 *res = std::ptr::null_mut();
318 }
319 0
320 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
321
322 let mut resource = ManagedCResource::new(
323 |res: *mut *mut i32| {
324 unsafe {
325 *res = resource_ptr;
326 }
327 0
328 },
329 cleanup,
330 false,
331 None,
332 );
333 assert!(resource.is_ok());
334
335 if let Ok(ref mut resource) = &mut resource {
336 assert!(resource.close().is_ok())
337 }
338
339 flag.store(false, Ordering::SeqCst);
341 drop(resource);
342 assert!(!flag.load(Ordering::SeqCst));
343 }
344
345 #[test]
346 fn test_drop_does_not_call_cleanup_if_check_for_is_closed_returns_true() {
347 let flag = Arc::new(AtomicBool::new(false));
348 let flag_clone = flag.clone();
349 let resource_ptr = make_resource(60);
350
351 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
352 flag_clone.store(true, Ordering::SeqCst);
353 unsafe {
354 *res = std::ptr::null_mut();
355 }
356 0
357 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
358
359 let check_fn = Some(|_res: *mut i32| -> bool { true } as fn(_) -> bool);
360
361 {
362 let _resource = ManagedCResource::new(
363 |res: *mut *mut i32| {
364 unsafe {
365 *res = resource_ptr;
366 }
367 0
368 },
369 cleanup,
370 false,
371 check_fn,
372 );
373 assert!(_resource.is_ok());
374 }
375 assert!(!flag.load(Ordering::SeqCst));
376 }
377
378 #[test]
379 fn test_drop_does_call_cleanup_if_check_for_is_closed_returns_false() {
380 let flag = Arc::new(AtomicBool::new(false));
381 let flag_clone = flag.clone();
382 let resource_ptr = make_resource(60);
383
384 let cleanup = Some(Box::new(move |res: *mut *mut i32| -> i32 {
385 flag_clone.store(true, Ordering::SeqCst);
386 unsafe {
387 *res = std::ptr::null_mut();
388 }
389 0
390 }) as Box<dyn FnMut(*mut *mut i32) -> i32>);
391
392 let check_fn = Some(|_res: *mut i32| -> bool { false } as fn(*mut i32) -> bool);
393
394 {
395 let _resource = ManagedCResource::new(
396 |res: *mut *mut i32| {
397 unsafe {
398 *res = resource_ptr;
399 }
400 0
401 },
402 cleanup,
403 false,
404 check_fn,
405 );
406 assert!(_resource.is_ok())
407 }
408 assert!(flag.load(Ordering::SeqCst));
409 }
410}