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 CUSTOM_RB_CODE: &str = include_str!("./rb_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, RB,
74 };
75 use proc_macro2::TokenStream;
76 use std::fs;
77
78 #[test]
79 #[cfg(not(target_os = "windows"))] fn media_driver() {
81 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/media-driver.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
91 let file = write_to_file(TokenStream::new(), true, "md.rs");
92
93 let bindings_copy = bindings.clone();
94 for handler in bindings.handlers.iter_mut() {
95 let _ = crate::generate_handlers(handler, &bindings_copy);
97 }
98 for (p, w) in bindings
99 .wrappers
100 .values()
101 .filter(|w| !w.type_name.contains("_t_") && w.type_name != "in_addr")
102 .enumerate()
103 {
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, "md.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 let t = trybuild::TestCases::new();
120 append_to_file(&file, "use bindings::*; mod bindings { ").unwrap();
121 append_to_file(&file, MEDIA_DRIVER_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 client() {
131 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/client.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 assert_eq!(
141 0,
142 bindings.methods.len(),
143 "expected all methods to have been matched {:#?}",
144 bindings.methods
145 );
146
147 let file = write_to_file(TokenStream::new(), true, "client.rs");
148 let bindings_copy = bindings.clone();
149 for handler in bindings.handlers.iter_mut() {
150 let _ = crate::generate_handlers(handler, &bindings_copy);
152 }
153 for (p, w) in bindings.wrappers.values().enumerate() {
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, "client.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
170 let t = trybuild::TestCases::new();
171 append_to_file(&file, "use bindings::*; mod bindings { ").unwrap();
172 append_to_file(&file, CLIENT_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 rb() {
182 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/rb.rs".into());
183
184 let file = write_to_file(TokenStream::new(), true, "rb.rs");
185
186 let bindings_copy = bindings.clone();
187 for handler in bindings.handlers.iter_mut() {
188 let _ = crate::generate_handlers(handler, &bindings_copy);
190 }
191
192 for (p, w) in bindings.wrappers.values().enumerate() {
193 let code = crate::generate_rust_code(
194 w,
195 &bindings.wrappers,
196 p == 0,
197 true,
198 false,
199 &bindings.handlers,
200 );
201 if code.to_string().contains("ndler : Option < AeronCloseClientHandlerImpl > , rbd :) -> Result < Self , AeronCError > { let resource = Manage") {
202 panic!("{}", format_token_stream(code));
203 }
204
205 write_to_file(code, false, "rb.rs");
206 }
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
214 let t = trybuild::TestCases::new();
215 append_to_file(&file, RB).unwrap();
216 append_to_file(&file, "\npub fn main() {}\n").unwrap();
217 t.pass(file)
218 }
219
220 #[test]
221 #[cfg(not(target_os = "windows"))] fn archive() {
223 let mut bindings = parse_bindings(&"../rusteron-code-gen/bindings/archive.rs".into());
224 assert_eq!(
225 "AeronImageFragmentAssembler",
226 bindings
227 .wrappers
228 .get("aeron_image_fragment_assembler_t")
229 .unwrap()
230 .class_name
231 );
232
233 let file = write_to_file(TokenStream::new(), true, "archive.rs");
234 let bindings_copy = bindings.clone();
235 for handler in bindings.handlers.iter_mut() {
236 let _ = crate::generate_handlers(handler, &bindings_copy);
238 }
239 for (p, w) in bindings.wrappers.values().enumerate() {
240 let code = crate::generate_rust_code(
241 w,
242 &bindings.wrappers,
243 p == 0,
244 true,
245 true,
246 &bindings.handlers,
247 );
248 write_to_file(code, false, "archive.rs");
249 }
250 let bindings_copy = bindings.clone();
251 for handler in bindings.handlers.iter_mut() {
252 let code = crate::generate_handlers(handler, &bindings_copy);
253 append_to_file(&file, &format_with_rustfmt(&code.to_string()).unwrap()).unwrap();
254 }
255 let t = trybuild::TestCases::new();
256 append_to_file(&file, "use bindings::*; mod bindings { ").unwrap();
257 append_to_file(&file, ARCHIVE_BINDINGS).unwrap();
258 append_to_file(&file, "}").unwrap();
259 append_to_file(&file, CUSTOM_AERON_CODE).unwrap();
260 append_to_file(&file, "\npub fn main() {}\n").unwrap();
261 t.pass(file)
262 }
263
264 fn write_to_file(rust_code: TokenStream, delete: bool, name: &str) -> String {
265 let src = format_token_stream(rust_code);
266 let path = format!("../target/{name}");
267 let path = &path;
268 if delete {
269 let _ = fs::remove_file(path);
270 }
271 append_to_file(path, &src).unwrap();
272 path.to_string()
273 }
274}