Skip to content

Commit

Permalink
Add extend method for simplifying multiple generated blocks into one
Browse files Browse the repository at this point in the history
  • Loading branch information
BenFordTytherington authored and ahayzen-kdab committed Feb 3, 2025
1 parent fc9b984 commit c8409c7
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 170 deletions.
5 changes: 4 additions & 1 deletion crates/cxx-qt-gen/src/generator/rust/cxxqttype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use syn::{parse_quote, Result};

use super::fragment::GeneratedRustFragment;

pub fn generate(qobject_names: &QObjectNames, type_names: &TypeNames) -> Result<GeneratedRustFragment> {
pub fn generate(
qobject_names: &QObjectNames,
type_names: &TypeNames,
) -> Result<GeneratedRustFragment> {
let cpp_struct_ident = &qobject_names.name.rust_unqualified();
let rust_struct_ident = &qobject_names.rust_struct.rust_unqualified();
let (rust_fn_name, rust_fn_attrs, rust_fn_qualified) = qobject_names
Expand Down
29 changes: 9 additions & 20 deletions crates/cxx-qt-gen/src/generator/rust/externcxxqt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{
generator::rust::{
fragment::GeneratedRustFragment,
signals::generate_rust_signal,
},
generator::rust::{fragment::GeneratedRustFragment, signals::generate_rust_signal},
naming::TypeNames,
parser::externcxxqt::ParsedExternCxxQt,
syntax::path::path_compare_str,
Expand All @@ -19,8 +16,6 @@ impl GeneratedRustFragment {
extern_cxxqt_block: &ParsedExternCxxQt,
type_names: &TypeNames,
) -> Result<Self> {
let mut generated = Self::default();

let extern_block_namespace = if let Some(namespace) = &extern_cxxqt_block.namespace {
quote! { #[namespace = #namespace ] }
} else {
Expand Down Expand Up @@ -66,27 +61,21 @@ impl GeneratedRustFragment {
})
.collect::<Vec<_>>();

generated.append(&mut GeneratedRustFragment {
cxx_mod_contents: vec![
parse_quote! {
#extern_block_namespace
#unsafety extern "C++" {
#(#items)*
let mut generated = vec![GeneratedRustFragment::from_cxx_item(parse_quote! {
#extern_block_namespace
#unsafety extern "C++" {
#(#items)*

#(#types)*
}
#(#types)*
}
],
cxx_qt_mod_contents: vec![],
});
})];

// Build the signals
for signal in &extern_cxxqt_block.signals {
let qobject_name = type_names.lookup(&signal.qobject_ident)?;

generated.append(&mut generate_rust_signal(signal, qobject_name, type_names)?);
generated.push(generate_rust_signal(signal, qobject_name, type_names)?);
}

Ok(generated)
Ok(GeneratedRustFragment::flatten(generated))
}
}
23 changes: 19 additions & 4 deletions crates/cxx-qt-gen/src/generator/rust/fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,24 @@ pub struct GeneratedRustFragment {
}

impl GeneratedRustFragment {
pub fn append(&mut self, other: &mut Self) {
self.cxx_mod_contents.append(&mut other.cxx_mod_contents);
self.cxx_qt_mod_contents
.append(&mut other.cxx_qt_mod_contents);
pub fn append(&mut self, other: Self) {
self.cxx_mod_contents.extend(other.cxx_mod_contents);
self.cxx_qt_mod_contents.extend(other.cxx_qt_mod_contents);
}

// Create a singular GeneratedRustFragment from a Vector of multiple
pub fn flatten(others: Vec<Self>) -> Self {
let mut this = Self::default();
for other in others {
this.append(other);
}
this
}

pub fn from_cxx_item(contents: Item) -> Self {
Self {
cxx_mod_contents: vec![contents],
..Default::default()
}
}
}
100 changes: 47 additions & 53 deletions crates/cxx-qt-gen/src/generator/rust/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,64 @@

use crate::generator::rust::get_params_tokens;
use crate::{
generator::{
naming::qobject::QObjectNames,
rust::fragment::GeneratedRustFragment,
},
generator::{naming::qobject::QObjectNames, rust::fragment::GeneratedRustFragment},
parser::method::ParsedMethod,
};
use quote::quote;
use syn::{parse_quote_spanned, spanned::Spanned, Result};

pub fn generate_rust_methods(
invokables: &Vec<&ParsedMethod>,
invokables: &[&ParsedMethod],
qobject_names: &QObjectNames,
) -> Result<GeneratedRustFragment> {
let mut generated = GeneratedRustFragment::default();
let cpp_class_name_rust = &qobject_names.name.rust_unqualified();

for &invokable in invokables {
// TODO: once we aren't using qobject::T in the extern "RustQt"
// we can just pass through the original ExternFn block and add the attribute?
let invokable_ident_cpp = invokable.name.cxx_unqualified();
let invokable_ident_rust = invokable.name.rust_unqualified();

let parameter_signatures = get_params_tokens(
invokable.mutable,
&invokable.parameters,
cpp_class_name_rust,
);

let return_type = &invokable.method.sig.output;

let mut unsafe_call = Some(quote! { unsafe });
if invokable.safe {
std::mem::swap(&mut unsafe_call, &mut None);
}

let cfgs = &invokable.cfgs;
let cxx_namespace = qobject_names.namespace_tokens();

generated.append(&mut GeneratedRustFragment {
cxx_mod_contents: vec![
parse_quote_spanned! {
invokable.method.span() =>
// Note: extern "Rust" block does not need to be unsafe
extern "Rust" {
// Note that we are exposing a Rust method on the C++ type to C++
//
// CXX ends up generating the source, then we generate the matching header.
#[cxx_name = #invokable_ident_cpp]
// Needed for QObjects to have a namespace on their type or extern block
//
// A Namespace from cxx_qt::bridge would be automatically applied to all children
// but to apply it to only certain types, it is needed here too
#cxx_namespace
#(#cfgs)*
#[doc(hidden)]
#unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type;
}
let generated = invokables
.iter()
.map(|invokable| {
// TODO: once we aren't using qobject::T in the extern "RustQt"
// we can just pass through the original ExternFn block and add the attribute?
let invokable_ident_cpp = invokable.name.cxx_unqualified();
let invokable_ident_rust = invokable.name.rust_unqualified();

let parameter_signatures = get_params_tokens(
invokable.mutable,
&invokable.parameters,
cpp_class_name_rust,
);

let return_type = &invokable.method.sig.output;

let mut unsafe_call = Some(quote! { unsafe });
if invokable.safe {
std::mem::swap(&mut unsafe_call, &mut None);
}

let cfgs = &invokable.cfgs;
let cxx_namespace = qobject_names.namespace_tokens();

GeneratedRustFragment::from_cxx_item(parse_quote_spanned! {
invokable.method.span() =>
// Note: extern "Rust" block does not need to be unsafe
extern "Rust" {
// Note that we are exposing a Rust method on the C++ type to C++
//
// CXX ends up generating the source, then we generate the matching header.
#[cxx_name = #invokable_ident_cpp]
// Needed for QObjects to have a namespace on their type or extern block
//
// A Namespace from cxx_qt::bridge would be automatically applied to all children
// but to apply it to only certain types, it is needed here too
#cxx_namespace
#(#cfgs)*
#[doc(hidden)]
#unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type;
}
],
cxx_qt_mod_contents: vec![],
});
}
})
})
.collect::<Vec<_>>();

Ok(generated)
Ok(GeneratedRustFragment::flatten(generated))
}

#[cfg(test)]
Expand Down Expand Up @@ -106,7 +100,7 @@ mod tests {
let qobject_names = create_qobjectname();

let generated =
generate_rust_methods(&invokables.iter().collect(), &qobject_names).unwrap();
generate_rust_methods(&invokables.iter().collect::<Vec<_>>(), &qobject_names).unwrap();

assert_eq!(generated.cxx_mod_contents.len(), 4);
assert_eq!(generated.cxx_qt_mod_contents.len(), 0);
Expand Down
38 changes: 17 additions & 21 deletions crates/cxx-qt-gen/src/generator/rust/property/getter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,26 @@ pub fn generate(
let cxx_namespace = qobject_names.namespace_tokens();

Ok(Some(GeneratedRustFragment {
cxx_mod_contents: vec![
parse_quote! {
extern "Rust" {
#[cxx_name = #getter_cpp]
// Needed for QObjects to have a namespace on their type or extern block
//
// A Namespace from cxx_qt::bridge would be automatically applied to all children
// but to apply it to only certain types, it is needed here too
#cxx_namespace
unsafe fn #getter_rust<'a>(self: &'a #cpp_class_name_rust) -> &'a #cxx_ty;
}
cxx_mod_contents: vec![parse_quote! {
extern "Rust" {
#[cxx_name = #getter_cpp]
// Needed for QObjects to have a namespace on their type or extern block
//
// A Namespace from cxx_qt::bridge would be automatically applied to all children
// but to apply it to only certain types, it is needed here too
#cxx_namespace
unsafe fn #getter_rust<'a>(self: &'a #cpp_class_name_rust) -> &'a #cxx_ty;
}
],
cxx_qt_mod_contents: vec![
parse_quote! {
impl #qualified_impl {
#[doc = "Getter for the Q_PROPERTY "]
#[doc = #ident_str]
pub fn #getter_rust(&self) -> &#qualified_ty {
&self.#ident
}
}],
cxx_qt_mod_contents: vec![parse_quote! {
impl #qualified_impl {
#[doc = "Getter for the Q_PROPERTY "]
#[doc = #ident_str]
pub fn #getter_rust(&self) -> &#qualified_ty {
&self.#ident
}
}
],
}],
}))
} else {
Ok(None)
Expand Down
12 changes: 6 additions & 6 deletions crates/cxx-qt-gen/src/generator/rust/property/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ pub fn generate_rust_properties(
for property in properties {
let idents = QPropertyNames::try_from_property(property, structured_qobject)?;

if let Some(mut getter) = getter::generate(&idents, qobject_names, &property.ty, type_names)? {
generated.append(&mut getter);
if let Some(getter) = getter::generate(&idents, qobject_names, &property.ty, type_names)? {
generated.append(getter);
};

if let Some(mut setter) = setter::generate(&idents, qobject_names, &property.ty, type_names)? {
generated.append(&mut setter);
if let Some(setter) = setter::generate(&idents, qobject_names, &property.ty, type_names)? {
generated.append(setter);
}

if let Some(notify) = signal::generate(&idents, qobject_names) {
signals.push(notify)
}
}

generated.append(&mut generate_rust_signals(
&signals.iter().collect(),
generated.append(generate_rust_signals(
&signals.iter().collect::<Vec<_>>(),
qobject_names,
type_names,
)?);
Expand Down
Loading

0 comments on commit c8409c7

Please sign in to comment.