33//
44// SPDX-License-Identifier: MIT OR Apache-2.0
55
6- use crate :: generator:: rust:: get_params_tokens;
6+ use crate :: generator:: rust:: {
7+ get_call_params_tokens, get_params_tokens, get_params_tokens_qualified,
8+ } ;
9+ use crate :: naming:: rust:: syn_type_cxx_bridge_to_qualified;
10+ use crate :: naming:: TypeNames ;
711use crate :: {
812 generator:: { naming:: qobject:: QObjectNames , rust:: fragment:: GeneratedRustFragment } ,
913 parser:: method:: ParsedMethod ,
1014} ;
1115use quote:: quote;
12- use syn:: { parse_quote_spanned, spanned:: Spanned , Result } ;
16+ use syn:: { parse_quote_spanned, spanned:: Spanned , Item , Result , ReturnType } ;
1317
1418pub fn generate_rust_methods (
1519 invokables : & [ & ParsedMethod ] ,
1620 qobject_names : & QObjectNames ,
21+ type_names : & TypeNames ,
1722) -> Result < GeneratedRustFragment > {
1823 let cpp_class_name_rust = & qobject_names. name . rust_unqualified ( ) ;
1924
@@ -34,6 +39,7 @@ pub fn generate_rust_methods(
3439 let return_type = & invokable. method . sig . output ;
3540
3641 let cfgs = & invokable. cfgs ;
42+
3743 let cxx_namespace = qobject_names. namespace_tokens ( ) ;
3844
3945 let ( block_type, block_safety) = if invokable. is_pure {
@@ -54,35 +60,101 @@ pub fn generate_rust_methods(
5460 Some ( quote ! { unsafe } )
5561 } ;
5662
57- GeneratedRustFragment :: from_cxx_item ( parse_quote_spanned ! {
58- invokable. method. span( ) =>
59- // Note: extern "Rust" block does not need to be unsafe
60- #block_safety extern #block_type {
61- // Note that we are exposing a Rust method on the C++ type to C++
62- //
63- // CXX ends up generating the source, then we generate the matching header.
64- #[ cxx_name = #invokable_ident_cpp]
65- // Needed for QObjects to have a namespace on their type or extern block
66- //
67- // A Namespace from cxx_qt::bridge would be automatically applied to all children
68- // but to apply it to only certain types, it is needed here too
69- #cxx_namespace
70- #( #cfgs) *
71- #[ doc( hidden) ]
72- #unsafe_call fn #invokable_ident_rust( #parameter_signatures) #return_type;
73- }
63+ let wrapper_fn = if invokable. wrap {
64+ vec ! [ generate_auto_wrap_fn( type_names, qobject_names, invokable) ?]
65+ } else {
66+ vec ! [ ]
67+ } ;
68+
69+ Ok ( GeneratedRustFragment {
70+ cxx_mod_contents : vec ! [ parse_quote_spanned! {
71+ invokable. method. span( ) =>
72+ // Note: extern "Rust" block does not need to be unsafe
73+ #block_safety extern #block_type {
74+ // Note that we are exposing a Rust method on the C++ type to C++
75+ //
76+ // CXX ends up generating the source, then we generate the matching header.
77+ #[ cxx_name = #invokable_ident_cpp]
78+ // Needed for QObjects to have a namespace on their type or extern block
79+ //
80+ // A Namespace from cxx_qt::bridge would be automatically applied to all children
81+ // but to apply it to only certain types, it is needed here too
82+ #cxx_namespace
83+ #( #cfgs) *
84+ #[ doc( hidden) ]
85+ #unsafe_call fn #invokable_ident_rust( #parameter_signatures) #return_type;
86+ }
87+ } ] ,
88+ cxx_qt_mod_contents : wrapper_fn,
7489 } )
7590 } )
76- . collect :: < Vec < _ > > ( ) ;
91+ . collect :: < Result < Vec < _ > > > ( ) ? ;
7792
7893 Ok ( GeneratedRustFragment :: flatten ( generated) )
7994}
8095
96+ pub fn generate_auto_wrap_fn (
97+ type_names : & TypeNames ,
98+ qobject_names : & QObjectNames ,
99+ invokable : & ParsedMethod ,
100+ ) -> Result < Item > {
101+ let docs = & invokable. docs ;
102+ let cfgs = & invokable. cfgs ;
103+
104+ let qualified_impl = type_names. rust_qualified ( & invokable. qobject_ident ) ?;
105+
106+ let invokable_ident_rust = invokable. name . rust_unqualified ( ) ;
107+
108+ let inner_fn = if invokable. mutable {
109+ quote ! { rust_mut( ) }
110+ } else {
111+ quote ! { rust( ) }
112+ } ;
113+
114+ let qualified_return_type = match & invokable. method . sig . output {
115+ ReturnType :: Default => ReturnType :: Default ,
116+ ReturnType :: Type ( arrow, boxed_type) => {
117+ let ty = boxed_type. as_ref ( ) ;
118+ let qualified_type = syn_type_cxx_bridge_to_qualified ( ty, type_names) ?;
119+ ReturnType :: Type ( * arrow, Box :: new ( qualified_type) )
120+ }
121+ } ;
122+
123+ let parameter_signatures_qualified = get_params_tokens_qualified (
124+ invokable. mutable ,
125+ & invokable. parameters ,
126+ & qobject_names. name . rust_qualified ( ) ,
127+ type_names,
128+ ) ?;
129+
130+ let call_parameters = get_call_params_tokens ( & invokable. parameters ) ;
131+
132+ let method_safety = if invokable. safe {
133+ None
134+ } else {
135+ Some ( quote ! { unsafe } )
136+ } ;
137+
138+ Ok ( parse_quote_spanned ! {
139+ invokable. method. span( ) =>
140+ #( #cfgs) *
141+ impl #qualified_impl {
142+ #( #docs) *
143+ pub #method_safety fn #invokable_ident_rust( #parameter_signatures_qualified) #qualified_return_type {
144+ use :: cxx_qt:: CxxQtType ;
145+
146+ self . #inner_fn. #invokable_ident_rust( #call_parameters)
147+ }
148+ }
149+ } )
150+ }
151+
81152#[ cfg( test) ]
82153mod tests {
83154 use super :: * ;
155+ use quote:: format_ident;
84156
85- use crate :: generator:: naming:: qobject:: tests:: create_qobjectname ;
157+ use crate :: generator:: naming:: qobject:: tests:: create_qobjectname_with_qcolor ;
86158 use crate :: tests:: assert_tokens_eq;
87159 use syn:: { parse_quote, ForeignItemFn } ;
88160
@@ -98,10 +170,12 @@ mod tests {
98170 } ;
99171 let method3: ForeignItemFn = parse_quote ! {
100172 #[ cxx_name = "opaqueInvokable" ]
173+ #[ auto_wrap]
101174 fn opaque_invokable( self : Pin <& mut MyObject >, param: & QColor ) -> UniquePtr <QColor >;
102175 } ;
103176 let method4: ForeignItemFn = parse_quote ! {
104177 #[ cxx_name = "unsafeInvokable" ]
178+ #[ auto_wrap]
105179 unsafe fn unsafe_invokable( self : & MyObject , param: * mut T ) -> * mut T ;
106180 } ;
107181 let invokables = vec ! [
@@ -110,13 +184,21 @@ mod tests {
110184 ParsedMethod :: mock_qinvokable( & method3) . make_mutable( ) ,
111185 ParsedMethod :: mock_qinvokable( & method4) . make_unsafe( ) ,
112186 ] ;
113- let qobject_names = create_qobjectname ( ) ;
187+ let qobject_names = create_qobjectname_with_qcolor ( ) ;
188+
189+ let mut type_names = TypeNames :: mock ( ) ;
190+ type_names. mock_insert ( "QColor" , Some ( format_ident ! ( "qobject" ) ) , None , None ) ;
191+ type_names. mock_insert ( "T" , Some ( format_ident ! ( "qobject" ) ) , None , None ) ;
114192
115- let generated =
116- generate_rust_methods ( & invokables. iter ( ) . collect :: < Vec < _ > > ( ) , & qobject_names) . unwrap ( ) ;
193+ let generated = generate_rust_methods (
194+ & invokables. iter ( ) . collect :: < Vec < _ > > ( ) ,
195+ & qobject_names,
196+ & type_names,
197+ )
198+ . unwrap ( ) ;
117199
118200 assert_eq ! ( generated. cxx_mod_contents. len( ) , 4 ) ;
119- assert_eq ! ( generated. cxx_qt_mod_contents. len( ) , 0 ) ;
201+ assert_eq ! ( generated. cxx_qt_mod_contents. len( ) , 2 ) ;
120202
121203 // void_invokable
122204 assert_tokens_eq (
@@ -154,6 +236,19 @@ mod tests {
154236 } ,
155237 ) ;
156238
239+ assert_tokens_eq (
240+ & generated. cxx_qt_mod_contents [ 0 ] ,
241+ quote ! {
242+ impl qobject:: MyObject {
243+ pub fn opaque_invokable( self : Pin <& mut qobject:: MyObject >, param: & qobject:: QColor ) -> cxx:: UniquePtr <qobject:: QColor > {
244+ use :: cxx_qt:: CxxQtType ;
245+
246+ self . rust_mut( ) . opaque_invokable( param)
247+ }
248+ }
249+ } ,
250+ ) ;
251+
157252 // unsafe_invokable
158253 assert_tokens_eq (
159254 & generated. cxx_mod_contents [ 3 ] ,
@@ -165,5 +260,18 @@ mod tests {
165260 }
166261 } ,
167262 ) ;
263+
264+ assert_tokens_eq (
265+ & generated. cxx_qt_mod_contents [ 1 ] ,
266+ quote ! {
267+ impl qobject:: MyObject {
268+ pub unsafe fn unsafe_invokable( self : & qobject:: MyObject , param: * mut qobject:: T ) -> * mut qobject:: T {
269+ use :: cxx_qt:: CxxQtType ;
270+
271+ self . rust( ) . unsafe_invokable( param)
272+ }
273+ }
274+ } ,
275+ ) ;
168276 }
169277}
0 commit comments