diff --git a/frb_codegen/assets/integration_template/REPLACE_ME_RUST_CRATE_DIR/src/frb_generated.rs b/frb_codegen/assets/integration_template/REPLACE_ME_RUST_CRATE_DIR/src/frb_generated.rs index b8a38566e4..637bdfa332 100644 --- a/frb_codegen/assets/integration_template/REPLACE_ME_RUST_CRATE_DIR/src/frb_generated.rs +++ b/frb_codegen/assets/integration_template/REPLACE_ME_RUST_CRATE_DIR/src/frb_generated.rs @@ -30,7 +30,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "REPLACE_ME_FRB_VERSION"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "REPLACE_ME_FRB_VERSION"; // Section: executor diff --git a/frb_codegen/src/library/codegen/generator/wire/rust/spec_generator/misc/mod.rs b/frb_codegen/src/library/codegen/generator/wire/rust/spec_generator/misc/mod.rs index c013d9bef2..1bd1ab603a 100644 --- a/frb_codegen/src/library/codegen/generator/wire/rust/spec_generator/misc/mod.rs +++ b/frb_codegen/src/library/codegen/generator/wire/rust/spec_generator/misc/mod.rs @@ -40,7 +40,7 @@ pub(crate) fn generate( code_header: Acc::new(|_| vec![(generate_code_header() + "\n\n").into()]), file_attributes: Acc::new_common(vec![FILE_ATTRIBUTES.to_string().into()]), imports: generate_imports(&cache.distinct_types, context), - executor: Acc::new_common(vec![generate_executor(context.ir_pack).into()]), + executor: Acc::new_common(vec![generate_handler(context.ir_pack).into()]), boilerplate: generate_boilerplate( context.config.default_stream_sink_codec, context.config.default_rust_opaque_codec, @@ -152,7 +152,7 @@ fn generate_boilerplate( default_rust_opaque = RustOpaque{default_rust_opaque_codec}, default_rust_auto_opaque = RustAutoOpaque{default_rust_opaque_codec}, ); - const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "{version}"; + pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "{version}"; "#, version = env!("CARGO_PKG_VERSION"), ) @@ -198,9 +198,9 @@ fn generate_boilerplate( // } // } -fn generate_executor(ir_pack: &IrPack) -> String { - if ir_pack.has_executor { - "/* nothing since executor detected */".to_owned() +fn generate_handler(ir_pack: &IrPack) -> String { + if let Some(existing_handler) = &ir_pack.existing_handler { + format!("pub use {};", existing_handler.rust_style()) } else { r#"flutter_rust_bridge::frb_generated_default_handler!();"#.to_owned() } diff --git a/frb_codegen/src/library/codegen/generator/wire/rust/spec_generator/misc/ty/dart_fn.rs b/frb_codegen/src/library/codegen/generator/wire/rust/spec_generator/misc/ty/dart_fn.rs index 7482100389..f8342af47c 100644 --- a/frb_codegen/src/library/codegen/generator/wire/rust/spec_generator/misc/ty/dart_fn.rs +++ b/frb_codegen/src/library/codegen/generator/wire/rust/spec_generator/misc/ty/dart_fn.rs @@ -3,6 +3,7 @@ use crate::codegen::generator::wire::rust::spec_generator::base::*; use crate::codegen::generator::wire::rust::spec_generator::misc::ty::WireRustGeneratorMiscTrait; use crate::codegen::generator::wire::rust::spec_generator::output_code::WireRustOutputCode; use crate::codegen::ir::ty::IrTypeTrait; +use crate::library::misc::consts::HANDLER_NAME; use itertools::Itertools; impl<'a> WireRustGeneratorMiscTrait for DartFnWireRustGenerator<'a> { @@ -32,7 +33,7 @@ impl<'a> WireRustGeneratorMiscTrait for DartFnWireRustGenerator<'a> { async fn body(dart_opaque: flutter_rust_bridge::DartOpaque, {parameter_names_and_types}) -> {return_type} {{ let args = vec![{into_dart_expressions}]; - let message = FLUTTER_RUST_BRIDGE_HANDLER.dart_fn_invoke(dart_opaque, args).await; + let message = {HANDLER_NAME}.dart_fn_invoke(dart_opaque, args).await; <{return_type}>::sse_decode_single(message) }} diff --git a/frb_codegen/src/library/codegen/ir/pack.rs b/frb_codegen/src/library/codegen/ir/pack.rs index 5b7cb548ee..23045b3f75 100644 --- a/frb_codegen/src/library/codegen/ir/pack.rs +++ b/frb_codegen/src/library/codegen/ir/pack.rs @@ -17,7 +17,7 @@ pub struct IrPack { pub funcs: Vec, pub struct_pool: IrStructPool, pub enum_pool: IrEnumPool, - pub has_executor: bool, + pub existing_handler: Option, pub unused_types: Vec, } diff --git a/frb_codegen/src/library/codegen/parser/misc.rs b/frb_codegen/src/library/codegen/parser/misc.rs index 1dbc7e77b9..c7cab847d5 100644 --- a/frb_codegen/src/library/codegen/parser/misc.rs +++ b/frb_codegen/src/library/codegen/parser/misc.rs @@ -1,3 +1,5 @@ +use crate::library::misc::consts::HANDLER_NAME; + pub(crate) fn parse_has_executor(source_rust_content: &str) -> bool { - source_rust_content.contains(&"static ref FLUTTER_RUST_BRIDGE_HANDLER".to_string()) + source_rust_content.contains(&format!("static {HANDLER_NAME}")) } diff --git a/frb_codegen/src/library/codegen/parser/mod.rs b/frb_codegen/src/library/codegen/parser/mod.rs index 948432e86e..b9ce02aed0 100644 --- a/frb_codegen/src/library/codegen/parser/mod.rs +++ b/frb_codegen/src/library/codegen/parser/mod.rs @@ -10,6 +10,7 @@ pub(crate) mod type_parser; mod unused_checker; use crate::codegen::dumper::Dumper; +use crate::codegen::ir::namespace::{Namespace, NamespacedName}; use crate::codegen::ir::pack::IrPack; use crate::codegen::misc::GeneratorProgressBarPack; use crate::codegen::parser::function_extractor::extract_generalized_functions_from_file; @@ -21,6 +22,8 @@ use crate::codegen::parser::type_alias_resolver::resolve_type_aliases; use crate::codegen::parser::type_parser::TypeParser; use crate::codegen::parser::unused_checker::get_unused_types; use crate::codegen::ConfigDumpContent; +use crate::library::misc::consts::HANDLER_NAME; +use anyhow::ensure; use itertools::Itertools; use log::trace; use std::path::{Path, PathBuf}; @@ -88,7 +91,22 @@ pub(crate) fn parse( .sorted_by_cached_key(|func| func.name.clone()) .collect_vec(); - let has_executor = (file_data_arr.iter()).any(|file| parse_has_executor(&file.content)); + let existing_handlers = (file_data_arr.iter()) + .filter(|file| parse_has_executor(&file.content)) + .map(|file| { + NamespacedName::new( + Namespace::new_from_rust_crate_path(&file.path, &config.rust_crate_dir).unwrap(), + HANDLER_NAME.to_owned(), + ) + }) + .collect_vec(); + ensure!( + existing_handlers.len() <= 1, + // frb-coverage:ignore-start + // This will stop the whole generator and tell the users, so we do not care about testing it + "Should have at most one custom handler" + ); + // frb-coverage:ignore-end let (struct_pool, enum_pool) = type_parser.consume(); @@ -96,7 +114,7 @@ pub(crate) fn parse( funcs: ir_funcs, struct_pool, enum_pool, - has_executor, + existing_handler: existing_handlers.first().cloned(), unused_types: vec![], }; diff --git a/frb_codegen/test_fixtures/library/codegen/parser/mod/generics/expect_ir.json b/frb_codegen/test_fixtures/library/codegen/parser/mod/generics/expect_ir.json index a207bb00a5..746df9e92b 100644 --- a/frb_codegen/test_fixtures/library/codegen/parser/mod/generics/expect_ir.json +++ b/frb_codegen/test_fixtures/library/codegen/parser/mod/generics/expect_ir.json @@ -105,6 +105,7 @@ "wrapper_name": null } }, + "existing_handler": null, "funcs": [ { "codec_mode_pack": { @@ -372,7 +373,6 @@ "rust_async": false } ], - "has_executor": false, "struct_pool": { "crate::api/MyGenericStruct": { "comments": [], diff --git a/frb_codegen/test_fixtures/library/codegen/parser/mod/methods/expect_ir.json b/frb_codegen/test_fixtures/library/codegen/parser/mod/methods/expect_ir.json index d673beedb2..e920878655 100644 --- a/frb_codegen/test_fixtures/library/codegen/parser/mod/methods/expect_ir.json +++ b/frb_codegen/test_fixtures/library/codegen/parser/mod/methods/expect_ir.json @@ -21,6 +21,7 @@ "wrapper_name": null } }, + "existing_handler": null, "funcs": [ { "codec_mode_pack": { @@ -221,7 +222,6 @@ "rust_async": false } ], - "has_executor": false, "struct_pool": { "crate::api/MyStruct": { "comments": [], diff --git a/frb_codegen/test_fixtures/library/codegen/parser/mod/multi_input_file/expect_ir.json b/frb_codegen/test_fixtures/library/codegen/parser/mod/multi_input_file/expect_ir.json index c7beee912c..e144166628 100644 --- a/frb_codegen/test_fixtures/library/codegen/parser/mod/multi_input_file/expect_ir.json +++ b/frb_codegen/test_fixtures/library/codegen/parser/mod/multi_input_file/expect_ir.json @@ -1,5 +1,6 @@ { "enum_pool": {}, + "existing_handler": null, "funcs": [ { "codec_mode_pack": { @@ -44,7 +45,6 @@ "rust_async": false } ], - "has_executor": false, "struct_pool": {}, "unused_types": [] } \ No newline at end of file diff --git a/frb_codegen/test_fixtures/library/codegen/parser/mod/non_qualified_names/expect_ir.json b/frb_codegen/test_fixtures/library/codegen/parser/mod/non_qualified_names/expect_ir.json index a7c6803062..02e7c29816 100644 --- a/frb_codegen/test_fixtures/library/codegen/parser/mod/non_qualified_names/expect_ir.json +++ b/frb_codegen/test_fixtures/library/codegen/parser/mod/non_qualified_names/expect_ir.json @@ -1,5 +1,6 @@ { "enum_pool": {}, + "existing_handler": null, "funcs": [ { "codec_mode_pack": { @@ -79,7 +80,6 @@ "rust_async": false } ], - "has_executor": false, "struct_pool": {}, "unused_types": [] } \ No newline at end of file diff --git a/frb_codegen/test_fixtures/library/codegen/parser/mod/qualified_names/expect_ir.json b/frb_codegen/test_fixtures/library/codegen/parser/mod/qualified_names/expect_ir.json index 0982f8e341..da7a018109 100644 --- a/frb_codegen/test_fixtures/library/codegen/parser/mod/qualified_names/expect_ir.json +++ b/frb_codegen/test_fixtures/library/codegen/parser/mod/qualified_names/expect_ir.json @@ -1,5 +1,6 @@ { "enum_pool": {}, + "existing_handler": null, "funcs": [ { "codec_mode_pack": { @@ -129,7 +130,6 @@ "rust_async": false } ], - "has_executor": false, "struct_pool": {}, "unused_types": [] } \ No newline at end of file diff --git a/frb_codegen/test_fixtures/library/codegen/parser/mod/simple/expect_ir.json b/frb_codegen/test_fixtures/library/codegen/parser/mod/simple/expect_ir.json index f05ddbf01f..2c35c712fb 100644 --- a/frb_codegen/test_fixtures/library/codegen/parser/mod/simple/expect_ir.json +++ b/frb_codegen/test_fixtures/library/codegen/parser/mod/simple/expect_ir.json @@ -1,5 +1,6 @@ { "enum_pool": {}, + "existing_handler": null, "funcs": [ { "codec_mode_pack": { @@ -23,7 +24,6 @@ "rust_async": false } ], - "has_executor": false, "struct_pool": {}, "unused_types": [] } \ No newline at end of file diff --git a/frb_codegen/test_fixtures/library/codegen/parser/mod/unused_struct_enum/expect_ir.json b/frb_codegen/test_fixtures/library/codegen/parser/mod/unused_struct_enum/expect_ir.json index 4cf309cc98..750f5418fd 100644 --- a/frb_codegen/test_fixtures/library/codegen/parser/mod/unused_struct_enum/expect_ir.json +++ b/frb_codegen/test_fixtures/library/codegen/parser/mod/unused_struct_enum/expect_ir.json @@ -1,7 +1,7 @@ { "enum_pool": {}, + "existing_handler": null, "funcs": [], - "has_executor": false, "struct_pool": {}, "unused_types": [ "crate::api/UnusedStruct", diff --git a/frb_codegen/test_fixtures/library/codegen/parser/mod/use_type_in_another_file/expect_ir.json b/frb_codegen/test_fixtures/library/codegen/parser/mod/use_type_in_another_file/expect_ir.json index 662bf530ee..cd5eed46bc 100644 --- a/frb_codegen/test_fixtures/library/codegen/parser/mod/use_type_in_another_file/expect_ir.json +++ b/frb_codegen/test_fixtures/library/codegen/parser/mod/use_type_in_another_file/expect_ir.json @@ -15,6 +15,7 @@ "wrapper_name": null } }, + "existing_handler": null, "funcs": [ { "codec_mode_pack": { @@ -85,7 +86,6 @@ "rust_async": false } ], - "has_executor": false, "struct_pool": { "crate::another_file/StructInAnotherFile": { "comments": [], diff --git a/frb_example/dart_build_rs/rust/src/frb_generated.rs b/frb_example/dart_build_rs/rust/src/frb_generated.rs index 028904ddaf..540e33da57 100644 --- a/frb_example/dart_build_rs/rust/src/frb_generated.rs +++ b/frb_example/dart_build_rs/rust/src/frb_generated.rs @@ -30,7 +30,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; // Section: executor diff --git a/frb_example/dart_minimal/rust/src/frb_generated.rs b/frb_example/dart_minimal/rust/src/frb_generated.rs index 028904ddaf..540e33da57 100644 --- a/frb_example/dart_minimal/rust/src/frb_generated.rs +++ b/frb_example/dart_minimal/rust/src/frb_generated.rs @@ -30,7 +30,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; // Section: executor diff --git a/frb_example/deliberate_bad/rust/src/frb_generated.rs b/frb_example/deliberate_bad/rust/src/frb_generated.rs index 08b7009684..b71447ca1a 100644 --- a/frb_example/deliberate_bad/rust/src/frb_generated.rs +++ b/frb_example/deliberate_bad/rust/src/frb_generated.rs @@ -30,7 +30,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; // Section: executor diff --git a/frb_example/flutter_via_create/rust/src/frb_generated.rs b/frb_example/flutter_via_create/rust/src/frb_generated.rs index 0828c5e97f..69c5db1ff8 100644 --- a/frb_example/flutter_via_create/rust/src/frb_generated.rs +++ b/frb_example/flutter_via_create/rust/src/frb_generated.rs @@ -30,7 +30,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; // Section: executor diff --git a/frb_example/flutter_via_integrate/rust/src/frb_generated.rs b/frb_example/flutter_via_integrate/rust/src/frb_generated.rs index 0828c5e97f..69c5db1ff8 100644 --- a/frb_example/flutter_via_integrate/rust/src/frb_generated.rs +++ b/frb_example/flutter_via_integrate/rust/src/frb_generated.rs @@ -30,7 +30,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; // Section: executor diff --git a/frb_example/gallery/rust/src/frb_generated.rs b/frb_example/gallery/rust/src/frb_generated.rs index cf7463216f..9250ac3514 100644 --- a/frb_example/gallery/rust/src/frb_generated.rs +++ b/frb_example/gallery/rust/src/frb_generated.rs @@ -30,7 +30,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; // Section: executor diff --git a/frb_example/pure_dart/rust/src/api/custom_handler.rs b/frb_example/pure_dart/rust/src/api/custom_handler.rs new file mode 100644 index 0000000000..15c73abbe4 --- /dev/null +++ b/frb_example/pure_dart/rust/src/api/custom_handler.rs @@ -0,0 +1,13 @@ +// FRB_INTERNAL_GENERATOR: {"forbiddenDuplicatorModes": ["sync", "rustAsync", "sse", "sync sse", "rustAsync sse"]} + +#[allow(unused_imports)] +use crate::frb_generated::FLUTTER_RUST_BRIDGE_CODEGEN_VERSION; + +// This file demonstrates how to use a custom handler. +// Usually there is no need for this, and the default handler is used. +// +// Here, for simplicity, we still generate code for default handler. +// But surely you can copy-paste the content of that macro and modify according to your needs. +flutter_rust_bridge::frb_generated_default_handler!(); + +// NOTE: For more tests about customizing handler types and contents, please visit `src/auxiliary/custom_handler.rs` diff --git a/frb_example/pure_dart/rust/src/api/mod.rs b/frb_example/pure_dart/rust/src/api/mod.rs index 7f6fc0e159..ab2bae65a9 100644 --- a/frb_example/pure_dart/rust/src/api/mod.rs +++ b/frb_example/pure_dart/rust/src/api/mod.rs @@ -9,6 +9,7 @@ pub mod comment; #[cfg(target_os = "non_existent_os")] pub mod conditionally_compiled_module; pub mod constructor; +pub mod custom_handler; pub mod customization; pub mod dart_dynamic; pub mod dart_fn; diff --git a/frb_example/pure_dart/rust/src/auxiliary/custom_handler.rs b/frb_example/pure_dart/rust/src/auxiliary/custom_handler.rs new file mode 100644 index 0000000000..dc06ddb794 --- /dev/null +++ b/frb_example/pure_dart/rust/src/auxiliary/custom_handler.rs @@ -0,0 +1,167 @@ +// This file mainly serves as compilation test + +#![allow(unused_variables)] +#![allow(dead_code)] + +use flutter_rust_bridge::for_generated::*; +use flutter_rust_bridge::*; +use std::future::Future; + +pub type MyUnmodifiedHandler = SimpleHandler< + SimpleExecutor, + NoOpErrorListener, +>; + +#[derive(Clone, Copy)] +pub struct MyCustomErrorListener; + +impl ErrorListener for MyCustomErrorListener { + fn on_error(&self, error: HandlerError) { + unimplemented!() + } +} + +pub struct MyCustomThreadPool; + +impl BaseThreadPool for MyCustomThreadPool { + fn execute(&self, job: F) + where + F: FnOnce() + Send + 'static, + { + unimplemented!() + } +} + +pub struct MyCustomAsyncRuntime; + +impl BaseAsyncRuntime for MyCustomAsyncRuntime { + fn spawn(&self, future: F) -> JoinHandle + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + unimplemented!() + } +} + +pub type MyCustomSimpleHandler = SimpleHandler< + SimpleExecutor, + MyCustomErrorListener, +>; + +pub struct MyCustomExecutor; + +impl Executor for MyCustomExecutor { + fn execute_normal(&self, task_info: TaskInfo, task: TaskFn) + where + TaskFn: FnOnce( + TaskContext, + ) -> Result + + Send + + 'static, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn execute_sync( + &self, + task_info: TaskInfo, + sync_task: SyncTaskFn, + ) -> Rust2DartCodec::Message + where + SyncTaskFn: FnOnce() -> Result, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn execute_async(&self, task_info: TaskInfo, task: TaskFn) + where + TaskFn: FnOnce(TaskContext) -> TaskRetFut + Send + 'static, + TaskRetFut: Future> + + TaskRetFutTrait, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } +} + +pub type MyCustomHandlerWithCustomExecutor = SimpleHandler; + +pub struct MyFullyCustomHandler; + +impl Handler for MyFullyCustomHandler { + fn wrap_normal( + &self, + task_info: TaskInfo, + prepare: PrepareFn, + ) where + PrepareFn: FnOnce() -> TaskFn, + TaskFn: FnOnce( + TaskContext, + ) -> Result + + Send + + 'static, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn wrap_sync( + &self, + task_info: TaskInfo, + sync_task: SyncTaskFn, + ) -> ::WireSyncRust2DartType + where + SyncTaskFn: FnOnce() -> Result, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn wrap_async( + &self, + task_info: TaskInfo, + prepare: PrepareFn, + ) where + PrepareFn: FnOnce() -> TaskFn, + TaskFn: FnOnce(TaskContext) -> TaskRetFut + Send + 'static, + TaskRetFut: Future> + + TaskRetFutTrait, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn dart_fn_invoke( + &self, + dart_fn: DartOpaque, + args: Vec, + ) -> DartFnFuture { + unimplemented!() + } + + fn dart_fn_handle_output(&self, call_id: i32, message: Dart2RustMessageSse) { + unimplemented!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_create_custom_handlers() { + MyUnmodifiedHandler::new_simple(Default::default()); + MyCustomSimpleHandler::new( + SimpleExecutor::new( + MyCustomErrorListener, + MyCustomThreadPool, + MyCustomAsyncRuntime, + ), + MyCustomErrorListener, + ); + MyCustomHandlerWithCustomExecutor::new(MyCustomExecutor, MyCustomErrorListener); + } +} diff --git a/frb_example/pure_dart/rust/src/auxiliary/mod.rs b/frb_example/pure_dart/rust/src/auxiliary/mod.rs index 1909213263..938fcda96d 100644 --- a/frb_example/pure_dart/rust/src/auxiliary/mod.rs +++ b/frb_example/pure_dart/rust/src/auxiliary/mod.rs @@ -1,4 +1,7 @@ pub mod benchmark_raw; +// For simplicity, only test non-wasm +#[cfg(not(target_family = "wasm"))] +pub mod custom_handler; pub mod new_module_system; pub mod old_module_system; pub mod protobuf_for_benchmark; diff --git a/frb_example/pure_dart/rust/src/frb_generated.rs b/frb_example/pure_dart/rust/src/frb_generated.rs index b17bcad0c0..5ff1939b8a 100644 --- a/frb_example/pure_dart/rust/src/frb_generated.rs +++ b/frb_example/pure_dart/rust/src/frb_generated.rs @@ -73,11 +73,11 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueNom, default_rust_auto_opaque = RustAutoOpaqueNom, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; // Section: executor -flutter_rust_bridge::frb_generated_default_handler!(); +pub use crate::api::custom_handler::FLUTTER_RUST_BRIDGE_HANDLER; // Section: wire_funcs diff --git a/frb_example/pure_dart_pde/rust/src/api/custom_handler.rs b/frb_example/pure_dart_pde/rust/src/api/custom_handler.rs new file mode 100644 index 0000000000..35b7dec5ab --- /dev/null +++ b/frb_example/pure_dart_pde/rust/src/api/custom_handler.rs @@ -0,0 +1,15 @@ +// AUTO-GENERATED FROM frb_example/pure_dart, DO NOT EDIT + +// FRB_INTERNAL_GENERATOR: {"forbiddenDuplicatorModes": ["sync", "rustAsync", "sse", "sync sse", "rustAsync sse"]} + +#[allow(unused_imports)] +use crate::frb_generated::FLUTTER_RUST_BRIDGE_CODEGEN_VERSION; + +// This file demonstrates how to use a custom handler. +// Usually there is no need for this, and the default handler is used. +// +// Here, for simplicity, we still generate code for default handler. +// But surely you can copy-paste the content of that macro and modify according to your needs. +flutter_rust_bridge::frb_generated_default_handler!(); + +// NOTE: For more tests about customizing handler types and contents, please visit `src/auxiliary/custom_handler.rs` diff --git a/frb_example/pure_dart_pde/rust/src/api/mod.rs b/frb_example/pure_dart_pde/rust/src/api/mod.rs index 4b49bab202..ba1c03aab7 100644 --- a/frb_example/pure_dart_pde/rust/src/api/mod.rs +++ b/frb_example/pure_dart_pde/rust/src/api/mod.rs @@ -9,6 +9,7 @@ pub mod comment; #[cfg(target_os = "non_existent_os")] pub mod conditionally_compiled_module; pub mod constructor; +pub mod custom_handler; pub mod customization; pub mod dart_fn; pub mod dart_opaque; diff --git a/frb_example/pure_dart_pde/rust/src/auxiliary/custom_handler.rs b/frb_example/pure_dart_pde/rust/src/auxiliary/custom_handler.rs new file mode 100644 index 0000000000..81ae5d20ed --- /dev/null +++ b/frb_example/pure_dart_pde/rust/src/auxiliary/custom_handler.rs @@ -0,0 +1,169 @@ +// AUTO-GENERATED FROM frb_example/pure_dart, DO NOT EDIT + +// This file mainly serves as compilation test + +#![allow(unused_variables)] +#![allow(dead_code)] + +use flutter_rust_bridge::for_generated::*; +use flutter_rust_bridge::*; +use std::future::Future; + +pub type MyUnmodifiedHandler = SimpleHandler< + SimpleExecutor, + NoOpErrorListener, +>; + +#[derive(Clone, Copy)] +pub struct MyCustomErrorListener; + +impl ErrorListener for MyCustomErrorListener { + fn on_error(&self, error: HandlerError) { + unimplemented!() + } +} + +pub struct MyCustomThreadPool; + +impl BaseThreadPool for MyCustomThreadPool { + fn execute(&self, job: F) + where + F: FnOnce() + Send + 'static, + { + unimplemented!() + } +} + +pub struct MyCustomAsyncRuntime; + +impl BaseAsyncRuntime for MyCustomAsyncRuntime { + fn spawn(&self, future: F) -> JoinHandle + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + unimplemented!() + } +} + +pub type MyCustomSimpleHandler = SimpleHandler< + SimpleExecutor, + MyCustomErrorListener, +>; + +pub struct MyCustomExecutor; + +impl Executor for MyCustomExecutor { + fn execute_normal(&self, task_info: TaskInfo, task: TaskFn) + where + TaskFn: FnOnce( + TaskContext, + ) -> Result + + Send + + 'static, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn execute_sync( + &self, + task_info: TaskInfo, + sync_task: SyncTaskFn, + ) -> Rust2DartCodec::Message + where + SyncTaskFn: FnOnce() -> Result, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn execute_async(&self, task_info: TaskInfo, task: TaskFn) + where + TaskFn: FnOnce(TaskContext) -> TaskRetFut + Send + 'static, + TaskRetFut: Future> + + TaskRetFutTrait, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } +} + +pub type MyCustomHandlerWithCustomExecutor = SimpleHandler; + +pub struct MyFullyCustomHandler; + +impl Handler for MyFullyCustomHandler { + fn wrap_normal( + &self, + task_info: TaskInfo, + prepare: PrepareFn, + ) where + PrepareFn: FnOnce() -> TaskFn, + TaskFn: FnOnce( + TaskContext, + ) -> Result + + Send + + 'static, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn wrap_sync( + &self, + task_info: TaskInfo, + sync_task: SyncTaskFn, + ) -> ::WireSyncRust2DartType + where + SyncTaskFn: FnOnce() -> Result, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn wrap_async( + &self, + task_info: TaskInfo, + prepare: PrepareFn, + ) where + PrepareFn: FnOnce() -> TaskFn, + TaskFn: FnOnce(TaskContext) -> TaskRetFut + Send + 'static, + TaskRetFut: Future> + + TaskRetFutTrait, + Rust2DartCodec: BaseCodec, + { + unimplemented!() + } + + fn dart_fn_invoke( + &self, + dart_fn: DartOpaque, + args: Vec, + ) -> DartFnFuture { + unimplemented!() + } + + fn dart_fn_handle_output(&self, call_id: i32, message: Dart2RustMessageSse) { + unimplemented!() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_create_custom_handlers() { + MyUnmodifiedHandler::new_simple(Default::default()); + MyCustomSimpleHandler::new( + SimpleExecutor::new( + MyCustomErrorListener, + MyCustomThreadPool, + MyCustomAsyncRuntime, + ), + MyCustomErrorListener, + ); + MyCustomHandlerWithCustomExecutor::new(MyCustomExecutor, MyCustomErrorListener); + } +} diff --git a/frb_example/pure_dart_pde/rust/src/auxiliary/mod.rs b/frb_example/pure_dart_pde/rust/src/auxiliary/mod.rs index fdfc029e0f..150728c003 100644 --- a/frb_example/pure_dart_pde/rust/src/auxiliary/mod.rs +++ b/frb_example/pure_dart_pde/rust/src/auxiliary/mod.rs @@ -1,6 +1,9 @@ // AUTO-GENERATED FROM frb_example/pure_dart, DO NOT EDIT pub mod benchmark_raw; +// For simplicity, only test non-wasm +#[cfg(not(target_family = "wasm"))] +pub mod custom_handler; pub mod new_module_system; pub mod old_module_system; pub mod protobuf_for_benchmark; diff --git a/frb_example/pure_dart_pde/rust/src/frb_generated.rs b/frb_example/pure_dart_pde/rust/src/frb_generated.rs index 3e461b8357..1182017674 100644 --- a/frb_example/pure_dart_pde/rust/src/frb_generated.rs +++ b/frb_example/pure_dart_pde/rust/src/frb_generated.rs @@ -46,11 +46,11 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_opaque = RustOpaqueMoi, default_rust_auto_opaque = RustAutoOpaqueMoi, ); -const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.30"; // Section: executor -flutter_rust_bridge::frb_generated_default_handler!(); +pub use crate::api::custom_handler::FLUTTER_RUST_BRIDGE_HANDLER; // Section: wire_funcs diff --git a/frb_rust/src/for_generated/mod.rs b/frb_rust/src/for_generated/mod.rs index 8d6ce93172..cbc16c411b 100644 --- a/frb_rust/src/for_generated/mod.rs +++ b/frb_rust/src/for_generated/mod.rs @@ -13,6 +13,7 @@ pub use crate::codec::dco::{transform_result_dco, Rust2DartMessageDco}; pub use crate::codec::sse::{ Dart2RustMessageSse, Rust2DartMessageSse, SseDeserializer, SseSerializer, }; +pub use crate::codec::Rust2DartMessageTrait; pub use crate::codec::{cst::CstCodec, dco::DcoCodec, sse::SseCodec, BaseCodec}; #[cfg(feature = "dart-opaque")] pub use crate::dart_opaque::dart2rust::{cst_decode_dart_opaque, sse_decode_dart_opaque}; @@ -20,7 +21,14 @@ pub use crate::generalized_arc::base_arc::BaseArc; pub use crate::generalized_arc::std_arc::StdArc; // TODO temp pub use crate::generalized_isolate::Channel; pub use crate::generalized_isolate::IntoDartExceptPrimitive; +pub use crate::handler::error::Error as HandlerError; +pub use crate::handler::error_listener::ErrorListener; +pub use crate::handler::executor::Executor; pub use crate::handler::handler::{FfiCallMode, TaskInfo}; +pub use crate::handler::handler::{TaskContext, TaskRetFutTrait}; +pub use crate::handler::implementation::error_listener::NoOpErrorListener; +pub use crate::handler::implementation::executor::SimpleExecutor; +pub use crate::handler::implementation::handler::SimpleHandler; pub use crate::misc::manual_impl::*; pub use crate::misc::version::FLUTTER_RUST_BRIDGE_RUNTIME_VERSION; pub use crate::platform_types::{ diff --git a/frb_rust/src/handler/implementation/mod.rs b/frb_rust/src/handler/implementation/mod.rs index 2f06c00fce..617fe86310 100644 --- a/frb_rust/src/handler/implementation/mod.rs +++ b/frb_rust/src/handler/implementation/mod.rs @@ -1,3 +1,3 @@ -mod error_listener; -mod executor; +pub(crate) mod error_listener; +pub(crate) mod executor; pub(crate) mod handler; diff --git a/website/docs/guides/custom/rust.md b/website/docs/guides/custom/rust.md index 0767a211be..1efc271236 100644 --- a/website/docs/guides/custom/rust.md +++ b/website/docs/guides/custom/rust.md @@ -2,11 +2,15 @@ ## Customize handler -By default, a `FLUTTER_RUST_BRIDGE_HANDLER` instance is automatically generated, +By default, a `FLUTTER_RUST_BRIDGE_HANDLER` instance is automatically generated +(by the auto-generated `flutter_rust_bridge::frb_generated_default_handler!()` macro), and you can see it at `frb_generated.rs`. -However, you can provide your own `FLUTTER_RUST_BRIDGE_HANDLER` lazy-static instance. +However, you can provide your own `FLUTTER_RUST_BRIDGE_HANDLER` instance. If your instance is detected, the generator will not generate one, but will use your version. +As for how to write a custom handler, often copy-paste-modify the code +in `flutter_rust_bridge::frb_generated_default_handler!()` is a good idea. + The handler is the central entrypoint to handle calls between Rust and Dart, therefore please visit the API of the `Handler` trait for more details. @@ -14,6 +18,11 @@ Some typical scenarios are: * [Inspection](../how-to/inspect) * [Report errors](../how-to/report-error) +* Customizing executors, thread pools, async runtimes, ... + +The API of handler may have breaking change across minor version bumps, +which is unlike most parts of flutter_rust_bridge which follows semantics versioning. +However, usually it can be easily migrated by adding the corresponding parameters in your code. ### States in handler