Skip to content

Commit

Permalink
cxx_qt::Constructor: Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonMatthesKDAB committed Jul 15, 2023
1 parent 26cb666 commit 5dc412f
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 40 deletions.
166 changes: 151 additions & 15 deletions crates/cxx-qt-gen/src/generator/cpp/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ 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}
{{
}}
"#,
Expand Down Expand Up @@ -62,15 +61,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 @@ -90,15 +89,15 @@ 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(", "),
},
});

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})"))
.collect::<Vec<_>>()
Expand All @@ -120,16 +119,153 @@ pub fn generate(
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));
}}
: {base_class}({base_args})
, m_rustObj({namespace_internals}::newRs{index}(std::move(args.newArguments))){initializers}
{{
{namespace_internals}::initialize{index}(*this, std::move(args.initializeArguments));
}}
"#,
},
})
}

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,
}
}

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

assert!(blocks.members.is_empty());
assert!(blocks.private_methods.is_empty());
assert!(blocks.metaobjects.is_empty());
assert!(blocks.forward_declares.is_empty());
assert!(blocks.deconstructors.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 test_default_constructor_no_initializers() {
let blocks = generate(
&qobject_for_testing(),
&[],
&[],
&ParsedCxxMappings::default(),
)
.unwrap();

assert!(blocks.members.is_empty());
assert!(blocks.private_methods.is_empty());
assert!(blocks.metaobjects.is_empty());
assert!(blocks.forward_declares.is_empty());
assert!(blocks.deconstructors.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 test_single_constructor() {
let blocks = generate(
&qobject_for_testing(),
&[Constructor {
arguments: vec![parse_quote! { i32 }, parse_quote! { *mut QObject }],
new_arguments: vec![],
base_arguments: vec![],
initialize_arguments: vec![],
// dummy
imp: parse_quote! {
impl X {}
},
}],
&[],
&ParsedCxxMappings::default(),
)
.unwrap();

assert!(blocks.members.is_empty());
assert!(blocks.forward_declares.is_empty());
assert!(blocks.deconstructors.is_empty());
assert!(blocks.metaobjects.is_empty());
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.newArguments)))
{{
rust::initialize0(*this, std::move(args.initializeArguments));
}}
"
),
}]
);
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)))
{{ }}
"
),
}]
);
}
}
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 @@ -5,6 +5,7 @@

use crate::generator::cpp::types::CppType;

#[derive(PartialEq, Eq, Debug)]
pub enum CppFragment {
Pair { header: String, source: String },
Header(String),
Expand Down
31 changes: 13 additions & 18 deletions crates/cxx-qt-gen/src/generator/rust/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,11 @@ const BASE_ARGUMENTS: &str = "CxxQtConstructorBaseArguments";
const NEW_ARGUMENTS: &str = "CxxQtConstructorNewArguments";
const INITIALIZE_ARGUMENTS: &str = "CxxQtConstructorInitializeArguments";

fn map_types<F: FnMut((usize, &Type)) -> TokenStream>(
args: &Option<Vec<Type>>,
f: F,
) -> Vec<TokenStream> {
args.as_ref()
.map(|args| args.iter().enumerate().map(f).collect())
.unwrap_or_default()
fn map_types<F: FnMut((usize, &Type)) -> TokenStream>(args: &[Type], f: F) -> Vec<TokenStream> {
args.iter().enumerate().map(f).collect()
}

fn extract_arguments_from_tuple(args: &Option<Vec<Type>>, tuple_name: Ident) -> Vec<TokenStream> {
fn extract_arguments_from_tuple(args: &[Type], tuple_name: Ident) -> Vec<TokenStream> {
map_types(args, |(index, _ty)| {
let arg_name = format_ident!("arg{index}");
let index = syn::LitInt::new(index.to_string().as_str(), Span::call_site());
Expand All @@ -40,7 +35,7 @@ fn extract_arguments_from_tuple(args: &Option<Vec<Type>>, tuple_name: Ident) ->
})
}

fn extract_arguments_from_struct(args: &Option<Vec<Type>>, struct_name: Ident) -> Vec<TokenStream> {
fn extract_arguments_from_struct(args: &[Type], struct_name: Ident) -> Vec<TokenStream> {
map_types(args, |(index, _ty)| {
let arg_name = format_ident!("arg{index}");
quote! {
Expand All @@ -49,7 +44,7 @@ fn extract_arguments_from_struct(args: &Option<Vec<Type>>, struct_name: Ident) -
})
}

fn argument_members(args: &Option<Vec<Type>>) -> Vec<TokenStream> {
fn argument_members(args: &[Type]) -> Vec<TokenStream> {
map_types(args, |(index, ty)| {
let arg_name = format_ident!("arg{index}");
quote! {
Expand Down Expand Up @@ -89,13 +84,13 @@ fn generate_default_constructor(
fn generate_arguments_struct(
namespace_internals: &str,
struct_name: &CombinedIdent,
argument_list: &Option<Vec<Type>>,
argument_list: &[Type],
) -> Item {
let argument_members = argument_members(argument_list);
let not_empty = if argument_list.as_ref().is_some_and(|list| !list.is_empty()) {
None
} else {
let not_empty = if argument_list.is_empty() {
Some(quote! { not_empty: i8 })
} else {
None
};
let rust_name = &struct_name.rust;
// use to_string here, as the cxx_name needs to be in quotes for the attribute macro.
Expand All @@ -115,14 +110,14 @@ fn generate_arguments_struct(
fn generate_arguments_initialization(
struct_name: &Ident,
instance_name: Ident,
argument_list: &Option<Vec<Type>>,
argument_list: &[Type],
) -> Expr {
let init_arguments = extract_arguments_from_tuple(argument_list, instance_name);
println!("init_arguments: {:?}", init_arguments);
let not_empty = if argument_list.as_ref().is_some_and(|list| !list.is_empty()) {
None
} else {
let not_empty = if argument_list.is_empty() {
Some(quote! { not_empty: 0 })
} else {
None
};

parse_quote! {
Expand Down
Loading

0 comments on commit 5dc412f

Please sign in to comment.