cxx_qt_gen/generator/rust/
mod.rs

1// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5
6pub mod constructor;
7pub mod cxxqttype;
8pub mod externcxxqt;
9pub mod fragment;
10pub mod inherit;
11pub mod method;
12pub mod property;
13pub mod qenum;
14pub mod qobject;
15pub mod signals;
16pub mod threading;
17
18use crate::generator::{rust::fragment::GeneratedRustFragment, structuring};
19use crate::parser::{parameter::ParsedFunctionParameter, Parser};
20use proc_macro2::{Ident, TokenStream};
21use quote::quote;
22use syn::{parse_quote, ItemMod, Result};
23
24/// Representation of the generated Rust code for a QObject
25pub struct GeneratedRustBlocks {
26    /// Module for the CXX bridge with passthrough items
27    pub cxx_mod: ItemMod,
28    /// Ident of the namespace of the QObject
29    pub namespace: String,
30    /// Rust fragments
31    pub fragments: Vec<GeneratedRustFragment>,
32}
33
34impl GeneratedRustBlocks {
35    /// Create a [GeneratedRustBlocks] from the given [Parser] object
36    pub fn from(parser: &Parser) -> Result<GeneratedRustBlocks> {
37        let structures = structuring::Structures::new(&parser.cxx_qt_data)?;
38
39        let mut fragments = vec![];
40        fragments.extend(
41            structures
42                .qobjects
43                .iter()
44                .map(|qobject| GeneratedRustFragment::from_qobject(qobject, &parser.type_names))
45                .collect::<Result<Vec<GeneratedRustFragment>>>()?,
46        );
47        fragments.extend(
48            parser
49                .cxx_qt_data
50                .extern_cxxqt_blocks
51                .iter()
52                .map(|extern_cxx_block| {
53                    GeneratedRustFragment::from_extern_cxx_qt(extern_cxx_block, &parser.type_names)
54                })
55                .collect::<Result<Vec<GeneratedRustFragment>>>()?,
56        );
57
58        let namespace = parser.cxx_qt_data.namespace.clone().unwrap_or_default();
59        let passthrough_mod = &parser.passthrough_module;
60
61        let vis = &passthrough_mod.vis;
62        let ident = &passthrough_mod.module_ident;
63        let docs = &passthrough_mod.docs;
64        let module = passthrough_mod.items.clone().map_or(
65            // If no items are present, curly braces aren't needed
66            quote! {
67                #vis mod #ident;
68            },
69            |items| {
70                quote! {
71                    #vis mod #ident {
72                        #(#items)*
73                    }
74                }
75            },
76        );
77        let cxx_mod = parse_quote! {
78            #[cxx::bridge(namespace = #namespace)]
79            // We need to allow for unused unsafe otherwise we get build failures
80            // due to changes in CXX 1.0.130
81            // https://github.com/dtolnay/cxx/commit/46fedc68464f80587057c436b4f6b6debeb9f714
82            #[allow(unused_unsafe)]
83            #(#docs)*
84            #module
85        };
86
87        let qenums = &parser.cxx_qt_data.qenums;
88        if !qenums.is_empty() {
89            fragments.extend(qenum::generate(qenums));
90        }
91
92        Ok(GeneratedRustBlocks {
93            cxx_mod,
94            namespace,
95            fragments,
96        })
97    }
98}
99
100/// Return the [TokenStream] of the parsed parameters for use in generation
101pub fn get_params_tokens(
102    mutable: bool,
103    parameters: &[ParsedFunctionParameter],
104    class_name: &Ident,
105) -> TokenStream {
106    let struct_sig = if mutable {
107        quote! { Pin<&mut #class_name> }
108    } else {
109        quote! { &#class_name }
110    };
111    if parameters.is_empty() {
112        quote! { self: #struct_sig }
113    } else {
114        let parameters = parameters
115            .iter()
116            .map(|parameter| {
117                let ident = &parameter.ident;
118                let ty = &parameter.ty;
119                quote! { #ident: #ty }
120            })
121            .collect::<Vec<TokenStream>>();
122        quote! { self: #struct_sig, #(#parameters),* }
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use syn::parse_quote;
130
131    #[test]
132    fn test_generated_rust_blocks() {
133        let module: ItemMod = parse_quote! {
134            #[cxx_qt::bridge]
135            mod ffi {
136                extern "RustQt" {
137                    #[qobject]
138                    type MyObject = super::MyObjectRust;
139                }
140            }
141        };
142        let parser = Parser::from(module).unwrap();
143
144        let rust = GeneratedRustBlocks::from(&parser).unwrap();
145        assert!(rust.cxx_mod.content.is_none());
146        assert_eq!(rust.namespace, "");
147        assert_eq!(rust.fragments.len(), 1);
148    }
149
150    #[test]
151    fn test_generated_rust_blocks_namespace() {
152        let module: ItemMod = parse_quote! {
153            #[cxx_qt::bridge(namespace = "cxx_qt")]
154            mod ffi {
155                extern "RustQt" {
156                    #[qobject]
157                    type MyObject = super::MyObjectRust;
158                }
159            }
160        };
161        let parser = Parser::from(module).unwrap();
162
163        let rust = GeneratedRustBlocks::from(&parser).unwrap();
164        assert!(rust.cxx_mod.content.is_none());
165        assert_eq!(rust.namespace, "cxx_qt");
166        assert_eq!(rust.fragments.len(), 1);
167    }
168
169    #[test]
170    fn test_generated_rust_blocks_foreign_qobject() {
171        let module: ItemMod = parse_quote! {
172            #[cxx_qt::bridge]
173            mod ffi {
174                extern "C++Qt" {
175                    #[qobject]
176                    type MyObject;
177                }
178            }
179        };
180        let parser = Parser::from(module).unwrap();
181
182        let rust = GeneratedRustBlocks::from(&parser).unwrap();
183        assert!(rust.cxx_mod.content.is_none());
184        assert_eq!(rust.namespace, "");
185        assert_eq!(rust.fragments.len(), 1);
186    }
187}