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

Make generated Dart opaque class abstract to improve testability #2032

Merged
merged 72 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
8e74d97
feat: demo
fzyzcjy Jun 4, 2024
a2c1567
feat: more
fzyzcjy Jun 4, 2024
7363870
feat: more
fzyzcjy Jun 4, 2024
c77cc4a
chore: rename
fzyzcjy Jun 4, 2024
c2fa1c0
feat: more
fzyzcjy Jun 4, 2024
8b2af84
Revert "feat: more"
fzyzcjy Jun 4, 2024
601d9f2
feat: pass around
fzyzcjy Jun 4, 2024
c656bbf
feat: more
fzyzcjy Jun 4, 2024
86e1e15
feat: more
fzyzcjy Jun 4, 2024
7dde214
feat: more
fzyzcjy Jun 4, 2024
e14d1dd
chore: empty
fzyzcjy Jun 4, 2024
01774a7
chore: mv
fzyzcjy Jun 4, 2024
b1c797d
feat: more
fzyzcjy Jun 4, 2024
d36e758
feat: more
fzyzcjy Jun 4, 2024
e8977d1
feat: more
fzyzcjy Jun 4, 2024
f2d5c1f
feat: more
fzyzcjy Jun 4, 2024
71aab1b
feat: more
fzyzcjy Jun 4, 2024
bb0d677
feat: more
fzyzcjy Jun 4, 2024
013f441
fix: err
fzyzcjy Jun 4, 2024
1265130
feat: more
fzyzcjy Jun 4, 2024
e659b7d
chore: rename
fzyzcjy Jun 4, 2024
625ed14
chore: simp
fzyzcjy Jun 4, 2024
d698d5b
fix: err
fzyzcjy Jun 4, 2024
1cb65bb
chore: codegen
fzyzcjy Jun 4, 2024
9cf6c55
feat: more
fzyzcjy Jun 4, 2024
b2a4c88
Merge branch 'feat/12149' into feat/12153
fzyzcjy Jun 4, 2024
a7ea971
fix: err
fzyzcjy Jun 4, 2024
b2f3f01
feat: more
fzyzcjy Jun 4, 2024
893279f
chore: codegen
fzyzcjy Jun 4, 2024
bb227c7
feat: more
fzyzcjy Jun 4, 2024
a18faf8
fix: err
fzyzcjy Jun 4, 2024
7a1f6f9
fix: more
fzyzcjy Jun 4, 2024
fe05858
chore: lint
fzyzcjy Jun 4, 2024
22374ea
chore: codegen
fzyzcjy Jun 4, 2024
5e56e9b
chore: more demo
fzyzcjy Jun 4, 2024
f78f9d9
feat: more
fzyzcjy Jun 4, 2024
216c3cd
feat: more
fzyzcjy Jun 4, 2024
d137a56
feat: more
fzyzcjy Jun 4, 2024
b4e0ef3
feat: more
fzyzcjy Jun 4, 2024
7f86e21
feat: more
fzyzcjy Jun 4, 2024
a3fb656
feat: more
fzyzcjy Jun 4, 2024
23f4f9e
feat: more
fzyzcjy Jun 4, 2024
69d15b8
feat: more
fzyzcjy Jun 4, 2024
428ff4d
feat: more
fzyzcjy Jun 4, 2024
c24854e
feat: more
fzyzcjy Jun 4, 2024
ac117ef
fix: err
fzyzcjy Jun 4, 2024
72692b3
chore: lint
fzyzcjy Jun 4, 2024
fc0c27c
chore: codegen
fzyzcjy Jun 4, 2024
86bed96
feat: more
fzyzcjy Jun 4, 2024
8340cb3
fix: err
fzyzcjy Jun 4, 2024
b9179d7
feat: more
fzyzcjy Jun 4, 2024
a934133
refactor: rm
fzyzcjy Jun 4, 2024
44ec72a
refactor: more
fzyzcjy Jun 4, 2024
94158c6
feat: more
fzyzcjy Jun 4, 2024
40d1ce4
chore: more
fzyzcjy Jun 4, 2024
0a22539
feat: more
fzyzcjy Jun 4, 2024
c9e6562
fix: err
fzyzcjy Jun 4, 2024
df314e1
chore: codegen
fzyzcjy Jun 4, 2024
4ca9731
feat: more
fzyzcjy Jun 4, 2024
ae106e0
chore: codegen
fzyzcjy Jun 4, 2024
40b9a71
Merge branch 'master' into feat/12153
fzyzcjy Jun 4, 2024
889f182
chore: codegen
fzyzcjy Jun 4, 2024
81bf891
chore: more
fzyzcjy Jun 4, 2024
c8392cc
chore: codegen
fzyzcjy Jun 4, 2024
7876d4a
chore: lint
fzyzcjy Jun 4, 2024
3598da8
chore: lint
fzyzcjy Jun 4, 2024
d71ba18
chore: codegen
fzyzcjy Jun 4, 2024
a1b67c1
feat: more
fzyzcjy Jun 4, 2024
996be2c
chore: codegen
fzyzcjy Jun 4, 2024
28e4037
feat: more sorted
fzyzcjy Jun 4, 2024
f3ac313
chore: codegen
fzyzcjy Jun 4, 2024
ad99b17
chore: more
fzyzcjy Jun 4, 2024
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
2 changes: 2 additions & 0 deletions frb_codegen/src/library/codegen/generator/api_dart/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use anyhow::Result;

pub(crate) struct GeneratorApiDartOutput {
pub output_texts: PathTexts,
pub output_extra_impl_text: String,
pub needs_freezed: bool,
}

Expand All @@ -33,6 +34,7 @@ pub(crate) fn generate(

Ok(GeneratorApiDartOutput {
output_texts: text.output_texts,
output_extra_impl_text: text.output_extra_impl_text,
needs_freezed: spec.namespaced_items.values().any(|x| x.needs_freezed),
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,21 @@
use convert_case::{Case, Casing};
use itertools::Itertools;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) enum GenerateApiMethodMode {
SeparatedDecl,
SeparatedImpl,
Combined,
}

pub(crate) fn generate_api_methods(
generalized_class_name: &NamespacedName,
context: ApiDartGeneratorContext,
mode: GenerateApiMethodMode,
) -> Vec<String> {
get_methods_of_enum_or_struct(generalized_class_name, &context.mir_pack.funcs)
.iter()
.map(|func| generate_api_method(func, context))
.filter_map(|func| generate_api_method(func, context, mode))
.collect_vec()
}

Expand Down Expand Up @@ -52,18 +60,30 @@
.collect_vec()
}

fn generate_api_method(func: &MirFunc, context: ApiDartGeneratorContext) -> String {
fn generate_api_method(
func: &MirFunc,
context: ApiDartGeneratorContext,
mode: GenerateApiMethodMode,
) -> Option<String> {
let api_dart_func = api_dart::spec_generator::function::generate(func, context).unwrap();

let method_info =
if_then_some!(let MirFuncOwnerInfo::Method(info) = &func.owner , info).unwrap();

if method_info.mode == MirFuncOwnerInfoMethodMode::Static
&& mode == GenerateApiMethodMode::SeparatedImpl
{
return None;
}

let default_constructor_mode = func.default_constructor_mode();

let skip_names = compute_skip_names(method_info);
let params = (api_dart_func.func_params.iter())
.filter(|param| !skip_names.contains(&&param.name_str[..]))
.cloned()
.collect_vec();
let method_name = generate_method_name(method_info, default_constructor_mode);

let comments = generate_comments(func, default_constructor_mode);
let signature = generate_signature(
Expand All @@ -72,10 +92,15 @@
&params,
default_constructor_mode,
&api_dart_func,
&method_name,
mode,
);
let implementation = generate_implementation(func, context, method_info, &params);

format!("{comments}{signature}=>{implementation};\n\n")
let maybe_implementation =
generate_maybe_implementation(func, context, method_info, &params, mode);
let maybe_implementation = (maybe_implementation.map(|x| format!("=>{x}"))).unwrap_or_default();

Some(format!("{comments}{signature}{maybe_implementation};\n\n"))
}

fn compute_skip_names(method_info: &MirFuncOwnerInfoMethod) -> Vec<&'static str> {
Expand Down Expand Up @@ -107,19 +132,12 @@
func_params: &[ApiDartGeneratedFunctionParam],
default_constructor_mode: Option<MirFuncDefaultConstructorMode>,
api_dart_func: &ApiDartGeneratedFunction,
method_name: &str,
mode: GenerateApiMethodMode,
) -> String {
let is_static_method = method_info.mode == MirFuncOwnerInfoMethodMode::Static;
let maybe_static = if is_static_method { "static" } else { "" };
let return_type = &api_dart_func.func_return_type;
let method_name = if default_constructor_mode.is_some() {
"newInstance".to_owned()
} else {
(method_info
.actual_method_dart_name
.as_ref()
.unwrap_or(&method_info.actual_method_name))
.to_case(Case::Camel)
};
let (func_params, maybe_accessor) = match func.accessor {
Some(MirFuncAccessorMode::Getter) => ("".to_owned(), "get"),
Some(MirFuncAccessorMode::Setter) => (
Expand All @@ -139,13 +157,51 @@
};

if default_constructor_mode == Some(MirFuncDefaultConstructorMode::DartConstructor) {
return format!("factory {return_type}{func_params}");
let owner_ty_name = method_info.owner_ty_name().unwrap().name;
let class_postfix = if mode == GenerateApiMethodMode::SeparatedImpl {
"Impl"

Check warning on line 162 in frb_codegen/src/library/codegen/generator/api_dart/spec_generator/class/method.rs

View check run for this annotation

Codecov / codecov/patch

frb_codegen/src/library/codegen/generator/api_dart/spec_generator/class/method.rs#L162

Added line #L162 was not covered by tests
} else {
""
};
return format!("factory {owner_ty_name}{class_postfix}{func_params}");
}

format!("{maybe_static} {return_type} {maybe_accessor} {method_name}{func_params}")
}

fn generate_implementation(
fn generate_method_name(
method_info: &MirFuncOwnerInfoMethod,
default_constructor_mode: Option<MirFuncDefaultConstructorMode>,
) -> String {
if default_constructor_mode.is_some() {
"newInstance".to_owned()
} else {
(method_info
.actual_method_dart_name
.as_ref()
.unwrap_or(&method_info.actual_method_name))
.to_case(Case::Camel)
}
}

fn generate_maybe_implementation(
func: &MirFunc,
context: ApiDartGeneratorContext,
method_info: &MirFuncOwnerInfoMethod,
params: &[ApiDartGeneratedFunctionParam],
mode: GenerateApiMethodMode,
) -> Option<String> {

Check warning on line 193 in frb_codegen/src/library/codegen/generator/api_dart/spec_generator/class/method.rs

View check run for this annotation

Codecov / codecov/patch

frb_codegen/src/library/codegen/generator/api_dart/spec_generator/class/method.rs#L187-L193

Added lines #L187 - L193 were not covered by tests
match (mode, method_info.mode.to_owned()) {
(GenerateApiMethodMode::Combined, _)
| (GenerateApiMethodMode::SeparatedDecl, MirFuncOwnerInfoMethodMode::Static)
| (GenerateApiMethodMode::SeparatedImpl, MirFuncOwnerInfoMethodMode::Instance) => Some(
generate_implementation_call_impl(func, context, method_info, params),
),
_ => None,
}
}

fn generate_implementation_call_impl(
func: &MirFunc,
context: ApiDartGeneratorContext,
method_info: &MirFuncOwnerInfoMethod,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::codegen::generator::api_dart::spec_generator::class::method::generate_api_methods;
use crate::codegen::generator::api_dart::spec_generator::class::method::{
generate_api_methods, GenerateApiMethodMode,
};
use crate::codegen::generator::api_dart::spec_generator::class::misc::generate_class_extra_body;
use crate::codegen::generator::api_dart::spec_generator::class::ty::ApiDartGeneratorClassTrait;
use crate::codegen::generator::api_dart::spec_generator::class::ApiDartGeneratedClass;
Expand All @@ -9,7 +11,9 @@ impl<'a> ApiDartGeneratorClassTrait for EnumRefApiDartGenerator<'a> {
fn generate_class(&self) -> Option<ApiDartGeneratedClass> {
let src = self.mir.get(self.context.mir_pack);

let methods_str = generate_api_methods(&src.name, self.context).join("\n");
let methods_str =
generate_api_methods(&src.name, self.context, GenerateApiMethodMode::Combined)
.join("\n");
let extra_body =
generate_class_extra_body(self.mir_type(), &self.context.mir_pack.dart_code_of_type);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ pub(crate) trait ApiDartGeneratorClassTrait {
fn generate_class(&self) -> Option<ApiDartGeneratedClass> {
None
}

/// The code will not be put in dart api files, but instead be in `frb_generated.dart`
fn generate_extra_impl_code(&self) -> Option<String> {
None
}
}

// the following have empty implementation
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::codegen::generator::api_dart::spec_generator::class::method::generate_api_methods;
use crate::codegen::generator::api_dart::spec_generator::class::method::{
generate_api_methods, GenerateApiMethodMode,
};
use crate::codegen::generator::api_dart::spec_generator::class::misc::generate_class_extra_body;
use crate::codegen::generator::api_dart::spec_generator::class::ty::ApiDartGeneratorClassTrait;
use crate::codegen::generator::api_dart::spec_generator::class::ApiDartGeneratedClass;
Expand All @@ -12,20 +14,13 @@ use regex::Regex;

impl<'a> ApiDartGeneratorClassTrait for RustOpaqueApiDartGenerator<'a> {
fn generate_class(&self) -> Option<ApiDartGeneratedClass> {
let dart_entrypoint_class_name = &self.context.config.dart_entrypoint_class_name;
let dart_api_instance = format!("{dart_entrypoint_class_name}.instance.api");
let Info {
dart_api_type,
methods,
} = self.compute_info(GenerateApiMethodMode::SeparatedDecl);

let rust_api_type = self.mir.rust_api_type();
let dart_api_type = ApiDartGenerator::new(self.mir.clone(), self.context).dart_api_type();

let methods = generate_api_methods(
&NamespacedName::new(
self.mir.namespace.clone(),
compute_api_method_query_name(&self.mir, self.context),
),
self.context,
)
.join("\n");
let extra_body =
generate_class_extra_body(self.mir_type(), &self.context.mir_pack.dart_code_of_type);

Expand All @@ -34,14 +29,43 @@ impl<'a> ApiDartGeneratorClassTrait for RustOpaqueApiDartGenerator<'a> {
class_name: dart_api_type.clone(),
code: format!(
"
// Rust type: {rust_api_type}
@sealed class {dart_api_type} extends RustOpaque {{
// Rust type: {rust_api_type}
abstract class {dart_api_type} {{
{methods}

void dispose();

bool get isDisposed;

{extra_body}
}}
"
),
needs_freezed: false,
header: Default::default(),
})
}

fn generate_extra_impl_code(&self) -> Option<String> {
let Info {
dart_api_type,
methods,
} = self.compute_info(GenerateApiMethodMode::SeparatedImpl);

let dart_api_type_impl = format!("{dart_api_type}Impl");

let dart_entrypoint_class_name = &self.context.config.dart_entrypoint_class_name;
let dart_api_instance = format!("{dart_entrypoint_class_name}.instance.api");

Some(format!(
"
@sealed class {dart_api_type_impl} extends RustOpaque implements {dart_api_type} {{
// Not to be used by end users
{dart_api_type}.frbInternalDcoDecode(List<dynamic> wire):
{dart_api_type_impl}.frbInternalDcoDecode(List<dynamic> wire):
super.frbInternalDcoDecode(wire, _kStaticData);

// Not to be used by end users
{dart_api_type}.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative):
{dart_api_type_impl}.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative):
super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData);

static final _kStaticData = RustArcStaticData(
Expand All @@ -51,15 +75,37 @@ impl<'a> ApiDartGeneratorClassTrait for RustOpaqueApiDartGenerator<'a> {
);

{methods}
{extra_body}
}}"
))
}
}

impl RustOpaqueApiDartGenerator<'_> {
fn compute_info(&self, mode: GenerateApiMethodMode) -> Info {
let dart_api_type = ApiDartGenerator::new(self.mir.clone(), self.context).dart_api_type();

let methods = generate_api_methods(
&NamespacedName::new(
self.mir.namespace.clone(),
compute_api_method_query_name(&self.mir, self.context),
),
needs_freezed: false,
header: Default::default(),
})
self.context,
mode,
)
.join("\n");

Info {
dart_api_type,
methods,
}
}
}

struct Info {
dart_api_type: String,
methods: String,
}

fn compute_api_method_query_name(
mir: &MirTypeRustOpaque,
_context: ApiDartGeneratorContext,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::codegen::generator::api_dart::spec_generator::class::method::{
dart_constructor_postfix, generate_api_methods,
dart_constructor_postfix, generate_api_methods, GenerateApiMethodMode,
};
use crate::codegen::generator::api_dart::spec_generator::class::misc::generate_class_extra_body;
use crate::codegen::generator::api_dart::spec_generator::class::ty::ApiDartGeneratorClassTrait;
Expand All @@ -15,7 +15,8 @@ impl<'a> ApiDartGeneratorClassTrait for StructRefApiDartGenerator<'a> {
let comments = generate_dart_comments(&src.comments);
let metadata = generate_dart_metadata(&src.dart_metadata);

let methods = generate_api_methods(&src.name, self.context);
let methods =
generate_api_methods(&src.name, self.context, GenerateApiMethodMode::Combined);
let extra_body =
generate_class_extra_body(self.mir_type(), &self.context.mir_pack.dart_code_of_type);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub(crate) struct ApiDartOutputSpec {
pub(crate) struct ApiDartOutputSpecItem {
pub funcs: Vec<ApiDartGeneratedFunction>,
pub classes: Vec<ApiDartGeneratedClass>,
pub extra_impl_code: Vec<String>,
pub imports: DartBasicHeaderCode,
pub preamble: String,
pub skips: Vec<MirSkip>,
Expand Down Expand Up @@ -116,20 +117,30 @@ fn generate_item(

let classes = namespaced_types
.map(|classes| {
classes
.iter()
(classes.iter())
.filter_map(|&ty| ApiDartGenerator::new(ty.clone(), context).generate_class())
.collect_vec()
})
.unwrap_or_default();

let extra_impl_code = namespaced_types
.map(|classes| {
(classes.iter())
.filter_map(|&ty| {
ApiDartGenerator::new(ty.clone(), context).generate_extra_impl_code()
})
.collect_vec()
})
.unwrap_or_default();

sanity_check_class_name_duplicates(&classes)?;

let needs_freezed = classes.iter().any(|class| class.needs_freezed);

Ok(ApiDartOutputSpecItem {
funcs,
classes,
extra_impl_code,
imports,
preamble: context.config.dart_preamble.clone(),
skips: compute_skips(context.mir_pack, namespace),
Expand Down
Loading
Loading