Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cxx_qt::Constructor: Add tests #608

Merged
merged 1 commit into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 290 additions & 21 deletions crates/cxx-qt-gen/src/generator/cpp/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ fn default_constructor(
r#"
{class_name}::{class_name}(QObject* parent)
: {base_class}(parent)
, m_rustObj({namespace_internals}::createRs())
{initializers}
{{
}}
, m_rustObj(::{namespace_internals}::createRs()){initializers}
{{ }}
"#,
class_name = qobject.ident,
base_class = qobject.base_class,
Expand Down Expand Up @@ -60,15 +58,15 @@ fn expand_arguments(arguments: &[Type], cxx_mappings: &ParsedCxxMappings) -> Res

pub fn generate(
qobject: &GeneratedCppQObject,
constructors: &Vec<Constructor>,
constructors: &[Constructor],
member_initializers: &[String],
cxx_mappings: &ParsedCxxMappings,
) -> Result<GeneratedCppQObjectBlocks> {
let initializers = member_initializers
.iter()
.map(|initializer| format!(", {initializer}"))
.map(|initializer| format!("\n , {initializer}"))
.collect::<Vec<_>>()
.join("\n");
.join("");

if constructors.is_empty() {
return Ok(default_constructor(qobject, initializers));
Expand All @@ -88,17 +86,17 @@ pub fn generate(
source: formatdoc! {
r#"
{class_name}::{class_name}({argument_list})
: {class_name}({namespace_internals}::routeArguments{index}({move_arguments}))
{{ }}
: {class_name}(::{namespace_internals}::routeArguments{index}({move_arguments}))
{{ }}
"#,
move_arguments = constructor_argument_names.iter().map(|arg| format!("std::move({arg})")).collect::<Vec<_>>().join(", "),
move_arguments = constructor_argument_names.iter().map(|arg| format!("::std::move({arg})")).collect::<Vec<_>>().join(", "),
},
});

let base_args = if let Some(base_args) = &constructor.base_arguments {
argument_names(base_args)
let base_args = if !constructor.base_arguments.is_empty() {
argument_names(&constructor.base_arguments)
.into_iter()
.map(|arg| format!("std::move(args.baseArguments.{arg})"))
.map(|arg| format!("::std::move(args.base.{arg})"))
.collect::<Vec<_>>()
.join(", ")
} else {
Expand All @@ -113,21 +111,292 @@ pub fn generate(
// can use it.
generated.private_methods.push(CppFragment::Pair {
header: format!(
"explicit {class_name}({namespace_internals}::CxxQtConstructorArguments{index}&& args);"
"explicit {class_name}(::{namespace_internals}::CxxQtConstructorArguments{index}&& args);"
),
source: formatdoc! {
r#"
{class_name}::{class_name}({namespace_internals}::CxxQtConstructorArguments{index}&& args)
: {base_class}({base_args})
, m_rustObj({namespace_internals}::newRs{index}(std::move(args.newArguments)))
{initializers}
{{
{namespace_internals}::initialize{index}(*this, std::move(args.initializeArguments));
}}
{class_name}::{class_name}(::{namespace_internals}::CxxQtConstructorArguments{index}&& args)
: {base_class}({base_args})
, m_rustObj(::{namespace_internals}::newRs{index}(::std::move(args.new_))){initializers}
{{
::{namespace_internals}::initialize{index}(*this, ::std::move(args.initialize));
}}
"#,
},
})
}

Ok(generated)
}

#[cfg(test)]
mod tests {
use super::*;

use std::assert_eq;
use syn::parse_quote;

fn qobject_for_testing() -> GeneratedCppQObject {
GeneratedCppQObject {
ident: "MyObject".to_string(),
rust_ident: "MyObjectQt".to_string(),
namespace_internals: "rust".to_string(),
base_class: "BaseClass".to_string(),
blocks: GeneratedCppQObjectBlocks::default(),
locking: true,
}
}

fn mock_constructor() -> Constructor {
Constructor {
arguments: vec![],
base_arguments: vec![],
new_arguments: vec![],
initialize_arguments: vec![],
imp: parse_quote! { impl X {} },
}
}

fn assert_empty_blocks(blocks: &GeneratedCppQObjectBlocks) {
assert!(blocks.members.is_empty());
assert!(blocks.metaobjects.is_empty());
assert!(blocks.forward_declares.is_empty());
assert!(blocks.deconstructors.is_empty());
}

#[test]
fn default_constructor_with_initializers() {
let blocks = generate(
&qobject_for_testing(),
&[],
&["member1(1)".to_string(), "member2{ 2 }".to_string()],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert!(blocks.private_methods.is_empty());
assert_eq!(
blocks.methods,
vec![CppFragment::Pair {
header: "explicit MyObject(QObject* parent = nullptr);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(QObject* parent)
: BaseClass(parent)
, m_rustObj(::rust::createRs())
, member1(1)
, member2{{ 2 }}
{{ }}
"
),
}]
);
}
#[test]
fn default_constructor_without_initializers() {
let blocks = generate(
&qobject_for_testing(),
&[],
&[],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert!(blocks.private_methods.is_empty());
assert_eq!(
blocks.methods,
vec![CppFragment::Pair {
header: "explicit MyObject(QObject* parent = nullptr);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(QObject* parent)
: BaseClass(parent)
, m_rustObj(::rust::createRs())
{{ }}
"
),
}]
);
}

#[test]
fn constructor_without_base_arguments() {
let blocks = generate(
&qobject_for_testing(),
&[Constructor {
arguments: vec![parse_quote! { i32 }, parse_quote! { *mut QObject }],
..mock_constructor()
}],
&[],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert_eq!(
blocks.private_methods,
vec![CppFragment::Pair {
header: "explicit MyObject(::rust::CxxQtConstructorArguments0&& args);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(::rust::CxxQtConstructorArguments0&& args)
: BaseClass()
, m_rustObj(::rust::newRs0(::std::move(args.new_)))
{{
::rust::initialize0(*this, ::std::move(args.initialize));
}}
"
),
}]
);
assert_eq!(
blocks.methods,
vec![CppFragment::Pair {
header: "explicit MyObject(::std::int32_t arg0, QObject* arg1);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(::std::int32_t arg0, QObject* arg1)
: MyObject(::rust::routeArguments0(::std::move(arg0), ::std::move(arg1)))
{{ }}
"
),
}]
);
}

#[test]
fn constructor_with_all_arguments() {
let blocks = generate(
&qobject_for_testing(),
&[Constructor {
arguments: vec![parse_quote! { i8 }, parse_quote! { i16 }],
new_arguments: vec![parse_quote! { i16}, parse_quote! { i32 }],
initialize_arguments: vec![parse_quote! { i32 }, parse_quote! { i64 }],
base_arguments: vec![parse_quote! { i64 }, parse_quote! { *mut QObject }],
..mock_constructor()
}],
&["initializer".to_string()],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert_eq!(
blocks.methods,
vec![CppFragment::Pair {
header: "explicit MyObject(::std::int8_t arg0, ::std::int16_t arg1);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(::std::int8_t arg0, ::std::int16_t arg1)
: MyObject(::rust::routeArguments0(::std::move(arg0), ::std::move(arg1)))
{{ }}
"
)
}]
);
assert_eq!(
blocks.private_methods,
vec![CppFragment::Pair {
header: "explicit MyObject(::rust::CxxQtConstructorArguments0&& args);".to_string(),
source: formatdoc!(
"
MyObject::MyObject(::rust::CxxQtConstructorArguments0&& args)
: BaseClass(::std::move(args.base.arg0), ::std::move(args.base.arg1))
, m_rustObj(::rust::newRs0(::std::move(args.new_)))
, initializer
{{
::rust::initialize0(*this, ::std::move(args.initialize));
}}
"
)
}]
);
}

#[test]
fn multiple_constructors() {
let blocks = generate(
&qobject_for_testing(),
&[
Constructor {
arguments: vec![],
..mock_constructor()
},
Constructor {
arguments: vec![parse_quote! { *mut QObject }],
base_arguments: vec![parse_quote! { *mut QObject }],
..mock_constructor()
},
],
&["initializer".to_string()],
&ParsedCxxMappings::default(),
)
.unwrap();

assert_empty_blocks(&blocks);
assert_eq!(blocks.methods.len(), 2);
assert_eq!(
blocks.methods,
vec![
CppFragment::Pair {
header: "explicit MyObject();".to_string(),
source: formatdoc!(
"
MyObject::MyObject()
: MyObject(::rust::routeArguments0())
{{ }}
"
),
},
CppFragment::Pair {
header: "explicit MyObject(QObject* arg0);".to_string(),
source: formatdoc! {
"
MyObject::MyObject(QObject* arg0)
: MyObject(::rust::routeArguments1(::std::move(arg0)))
{{ }}
"
}
}
]
);
assert_eq!(blocks.private_methods.len(), 2);
assert_eq!(
blocks.private_methods,
vec![
CppFragment::Pair {
header: "explicit MyObject(::rust::CxxQtConstructorArguments0&& args);"
.to_string(),
source: formatdoc!(
"
MyObject::MyObject(::rust::CxxQtConstructorArguments0&& args)
: BaseClass()
, m_rustObj(::rust::newRs0(::std::move(args.new_)))
, initializer
{{
::rust::initialize0(*this, ::std::move(args.initialize));
}}
"
)
},
CppFragment::Pair {
header: "explicit MyObject(::rust::CxxQtConstructorArguments1&& args);"
.to_string(),
source: formatdoc!(
"
MyObject::MyObject(::rust::CxxQtConstructorArguments1&& args)
: BaseClass(::std::move(args.base.arg0))
, m_rustObj(::rust::newRs1(::std::move(args.new_)))
, initializer
{{
::rust::initialize1(*this, ::std::move(args.initialize));
}}
"
)
}
]
);
}
}
1 change: 1 addition & 0 deletions crates/cxx-qt-gen/src/generator/cpp/fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

#[derive(PartialEq, Eq, Debug)]
pub enum CppFragment {
Pair { header: String, source: String },
Header(String),
Expand Down
21 changes: 13 additions & 8 deletions crates/cxx-qt-gen/src/generator/naming/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,24 @@ pub struct QObjectName {

impl From<&ParsedQObject> for QObjectName {
fn from(qobject: &ParsedQObject) -> Self {
let ident_left = &qobject.qobject_ty.ident_left;
Self::from_idents(
qobject.qobject_ty.ident_left.clone(),
qobject.qobject_ty.ident_right.clone(),
)
}
}

impl QObjectName {
pub fn from_idents(ident_left: Ident, ident_right: Ident) -> Self {
Self {
ident: ident_left.clone(),
// TODO: later we may support cxx_name or rust_name so keep these are CombinedIdents for now
cpp_class: CombinedIdent::from_ident(ident_left.clone()),
rust_struct: CombinedIdent::from_ident(qobject.qobject_ty.ident_right.clone()),
cxx_qt_thread_class: cxx_qt_thread_class_from_ident(ident_left),
cxx_qt_thread_queued_fn_struct: cxx_qt_thread_queued_fn_struct_from_ident(ident_left),
rust_struct: CombinedIdent::from_ident(ident_right),
cxx_qt_thread_class: cxx_qt_thread_class_from_ident(&ident_left),
cxx_qt_thread_queued_fn_struct: cxx_qt_thread_queued_fn_struct_from_ident(&ident_left),
ident: ident_left,
}
}
}

impl QObjectName {
/// For a given ident generate the mangled threading suffix ident
pub fn cxx_qt_thread_method(&self, suffix: &str) -> Ident {
format_ident!(
Expand Down
Loading