cxx_qt_gen/writer/rust/
mod.rs1use crate::generator::rust::GeneratedRustBlocks;
7use proc_macro2::TokenStream;
8use quote::{quote, ToTokens};
9use syn::{parse_quote_spanned, spanned::Spanned};
10
11pub fn write_rust(generated: &GeneratedRustBlocks, include_path: Option<&str>) -> TokenStream {
13 let mut cxx_mod = generated.cxx_mod.clone();
15 let mut cxx_mod_contents = vec![];
16 let mut cxx_qt_mod_contents = vec![];
17
18 cxx_mod_contents.insert(
20 0,
21 syn::parse2(signal_boilerplate()).expect("Could not build CXX common block"),
22 );
23
24 if let Some(include_path) = include_path {
32 let import_path = format!("{include_path}.cxxqt.h");
33 cxx_mod_contents.insert(
34 1,
35 parse_quote_spanned! {cxx_mod.span() =>
36 unsafe extern "C++" {
37 include!(#import_path);
38 }
39 },
40 );
41 }
42
43 for fragment in &generated.fragments {
44 cxx_mod_contents.extend_from_slice(&fragment.cxx_mod_contents);
46 cxx_qt_mod_contents.extend_from_slice(&fragment.cxx_qt_mod_contents);
47 }
48
49 if let Some((_, items)) = &mut cxx_mod.content {
51 items.extend(cxx_mod_contents);
52 } else {
53 cxx_mod.content = Some((syn::token::Brace::default(), cxx_mod_contents));
54 cxx_mod.semi = None;
55 }
56
57 quote! {
58 #cxx_mod
59
60 #(#cxx_qt_mod_contents)*
61 }
62 .into_token_stream()
63}
64
65fn signal_boilerplate() -> TokenStream {
66 quote! {
67 unsafe extern "C++" {
68 include ! (< QtCore / QObject >);
69
70 include!("cxx-qt/connection.h");
71 #[doc(hidden)]
72 #[namespace = "Qt"]
73 #[rust_name = "CxxQtConnectionType"]
74 #[allow(dead_code)]
75 type ConnectionType = cxx_qt::ConnectionType;
76
77 #[doc(hidden)]
78 #[namespace = "rust::cxxqt1"]
79 #[rust_name = "CxxQtQMetaObjectConnection"]
80 #[allow(dead_code)]
81 type QMetaObjectConnection = cxx_qt::QMetaObjectConnection;
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 use crate::generator::rust::fragment::GeneratedRustFragment;
91 use crate::Parser;
92 use pretty_assertions::assert_str_eq;
93 use syn::{parse_quote, ItemMod};
94
95 pub fn create_generated_rust() -> GeneratedRustBlocks {
97 GeneratedRustBlocks {
98 cxx_mod: parse_quote! {
99 #[cxx::bridge(namespace = "cxx_qt::my_object")]
100 mod ffi {}
101 },
102 namespace: "cxx_qt::my_object".to_owned(),
103 fragments: vec![GeneratedRustFragment {
104 cxx_mod_contents: vec![
105 parse_quote! {
106 unsafe extern "C++" {
107 type MyObject;
108 }
109 },
110 parse_quote! {
111 extern "Rust" {
112 type MyObjectRust;
113 }
114 },
115 ],
116 cxx_qt_mod_contents: vec![
117 parse_quote! {
118 #[derive(Default)]
119 pub struct MyObjectRust;
120 },
121 parse_quote! {
122 impl MyObjectRust {
123 fn rust_method(&self) {
124
125 }
126 }
127 },
128 ],
129 }],
130 }
131 }
132
133 pub fn create_generated_rust_multi_qobjects() -> GeneratedRustBlocks {
135 GeneratedRustBlocks {
136 cxx_mod: parse_quote! {
137 #[cxx::bridge(namespace = "cxx_qt")]
138 mod ffi {}
139 },
140 namespace: "cxx_qt".to_owned(),
141 fragments: vec![
142 GeneratedRustFragment {
143 cxx_mod_contents: vec![
144 parse_quote! {
145 unsafe extern "C++" {
146 type FirstObject;
147 }
148 },
149 parse_quote! {
150 extern "Rust" {
151 type FirstObjectRust;
152 }
153 },
154 ],
155 cxx_qt_mod_contents: vec![
156 parse_quote! {
157 #[derive(Default)]
158 pub struct FirstObjectRust;
159 },
160 parse_quote! {
161 impl FirstObjectRust {
162 fn rust_method(&self) {
163
164 }
165 }
166 },
167 ],
168 },
169 GeneratedRustFragment {
170 cxx_mod_contents: vec![
171 parse_quote! {
172 unsafe extern "C++" {
173 type SecondObject;
174 }
175 },
176 parse_quote! {
177 extern "Rust" {
178 type SecondObjectRust;
179 }
180 },
181 ],
182 cxx_qt_mod_contents: vec![
183 parse_quote! {
184 #[derive(Default)]
185 pub struct SecondObjectRust;
186 },
187 parse_quote! {
188 impl SecondObjectRust {
189 fn rust_method(&self) {
190
191 }
192 }
193 },
194 ],
195 },
196 ],
197 }
198 }
199
200 pub fn expected_rust() -> String {
202 let signal_boilerplate = signal_boilerplate();
203 quote! {
204 #[cxx::bridge(namespace = "cxx_qt::my_object")]
205 mod ffi {
206 #signal_boilerplate
207
208 unsafe extern "C++" {
209 include!("myobject.cxxqt.h");
210 }
211
212 unsafe extern "C++" {
213 type MyObject;
214 }
215
216 extern "Rust" {
217 type MyObjectRust;
218 }
219 }
220
221 #[derive(Default)]
222 pub struct MyObjectRust;
223
224 impl MyObjectRust {
225 fn rust_method(&self) {
226
227 }
228 }
229 }
230 .into_token_stream()
231 .to_string()
232 }
233
234 pub fn expected_rust_multi_qobjects() -> String {
236 let signal_boilerplate = signal_boilerplate();
237 quote! {
238 #[cxx::bridge(namespace = "cxx_qt")]
239 mod ffi {
240 #signal_boilerplate
241
242 unsafe extern "C++" {
243 include!("multiobject.cxxqt.h");
244 }
245
246 unsafe extern "C++" {
247 type FirstObject;
248 }
249
250 extern "Rust" {
251 type FirstObjectRust;
252 }
253
254 unsafe extern "C++" {
255 type SecondObject;
256 }
257
258 extern "Rust" {
259 type SecondObjectRust;
260 }
261 }
262
263 #[derive(Default)]
264 pub struct FirstObjectRust;
265
266 impl FirstObjectRust {
267 fn rust_method(&self) {
268
269 }
270 }
271
272 #[derive(Default)]
273 pub struct SecondObjectRust;
274
275 impl SecondObjectRust {
276 fn rust_method(&self) {
277
278 }
279 }
280 }
281 .into_token_stream()
282 .to_string()
283 }
284
285 #[test]
286 fn test_write_rust() {
287 let generated = create_generated_rust();
288 let result = write_rust(&generated, Some("myobject"));
289 assert_str_eq!(result.to_string(), expected_rust());
290 }
291
292 #[test]
293 fn test_write_rust_empty_mod() {
294 let module: ItemMod = parse_quote! {
295 #[cxx_qt::bridge]
296 mod ffi;
297 };
298 let parser = Parser::from(module).unwrap();
299
300 let generated = GeneratedRustBlocks::from(&parser).unwrap();
301 write_rust(&generated, None);
302 }
303
304 #[test]
305 fn test_write_rust_multi_qobjects() {
306 let generated = create_generated_rust_multi_qobjects();
307 let result = write_rust(&generated, Some("multiobject"));
308 assert_str_eq!(result.to_string(), expected_rust_multi_qobjects());
309 }
310}