diff --git a/Cargo.lock b/Cargo.lock index d72441fe686..d082fc5e34e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,6 +456,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "either" version = "1.9.0" @@ -617,6 +623,7 @@ dependencies = [ "notify-debouncer-mini", "paste", "pathdiff", + "pretty_assertions", "proc-macro2", "quote", "regex", @@ -1146,6 +1153,16 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -1853,3 +1870,9 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/frb_codegen/Cargo.toml b/frb_codegen/Cargo.toml index 87d1d34ed32..cd3bd50a1a0 100644 --- a/frb_codegen/Cargo.toml +++ b/frb_codegen/Cargo.toml @@ -55,6 +55,7 @@ notify = "6.1.1" notify-debouncer-mini = "0.4.1" [dev-dependencies] +pretty_assertions = "1.4.0" semver = "1.0.12" [package.metadata.binstall] diff --git a/frb_codegen/src/library/codegen/generator/api_dart/mod.rs b/frb_codegen/src/library/codegen/generator/api_dart/mod.rs index ff6b21469e4..b58353481cc 100644 --- a/frb_codegen/src/library/codegen/generator/api_dart/mod.rs +++ b/frb_codegen/src/library/codegen/generator/api_dart/mod.rs @@ -56,6 +56,12 @@ mod tests { body("library/codegen/generator/api_dart/mod/simple") } + #[test] + #[serial] + fn test_functions() -> anyhow::Result<()> { + body("library/codegen/generator/api_dart/mod/functions") + } + fn body(fixture_name: &str) -> anyhow::Result<()> { configure_opinionated_test_logging(); let test_fixture_dir = get_test_fixture_dir(fixture_name); diff --git a/frb_codegen/src/library/commands/cargo_expand.rs b/frb_codegen/src/library/commands/cargo_expand.rs index b1a62dc6a78..8ad567b5f87 100644 --- a/frb_codegen/src/library/commands/cargo_expand.rs +++ b/frb_codegen/src/library/commands/cargo_expand.rs @@ -2,14 +2,17 @@ use crate::codegen::dumper::Dumper; use crate::codegen::ConfigDumpContent; use crate::command_args; use crate::library::commands::command_runner::execute_command; + use anyhow::{bail, Context, Result}; use itertools::Itertools; +use lazy_static::lazy_static; use log::{debug, info, warn}; use regex::Regex; + +use std::borrow::Cow; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::HashMap; use std::path::{Path, PathBuf}; -use std::sync::OnceLock; use std::{env, fs}; #[derive(Default)] @@ -41,7 +44,10 @@ impl CachedCargoExpand { let expanded = match self.cache.entry(rust_crate_dir.to_owned()) { Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(run_cargo_expand(rust_crate_dir, dumper, true)?), + Vacant(entry) => entry.insert( + unwrap_frb_attrs_in_doc(&run_cargo_expand(rust_crate_dir, dumper, true)?) + .into_owned(), + ), }; extract_module(expanded, module) @@ -93,7 +99,7 @@ fn run_cargo_expand( "--theme=none", "--ugly", "--config", - "build.rustflags=\"--cfg frb_expand\"" + r#"build.rustflags="--cfg frb_expand""# ); let output = execute_command("cargo", &args, Some(rust_crate_dir), None) @@ -114,20 +120,22 @@ fn run_cargo_expand( // frb-coverage:ignore-end } - let mut stdout_lines = stdout.lines(); - stdout_lines.next(); - let ans = stdout_lines.join("\n"); - static PATTERN: OnceLock = OnceLock::new(); - let pattern = PATTERN.get_or_init(|| { - Regex::new(r####"#\[doc =[\s\n]*r###"frb_marker: ([\s\S]*?)"###]"####).unwrap() - }); - let ans = pattern.replace_all(&ans, "$1").into_owned(); - + let ans = stdout.lines().skip(1).join("\n"); dumper.dump_str(ConfigDumpContent::Source, "cargo_expand.rs", &ans)?; - Ok(ans) } +/// Turns `#[doc = "frb_marker: .."]` back into `#[frb(..)]`, usually produced +/// as a side-effect of cargo-expand. +// NOTE: The amount of pounds must match exactly with the implementation in frb_macros +fn unwrap_frb_attrs_in_doc(code: &str) -> Cow { + lazy_static! { + static ref PATTERN: Regex = + Regex::new(r####"#\[doc =[\s\n]*r###"frb_marker: ([\s\S]*?)"###]"####).unwrap(); + } + PATTERN.replace_all(code, "$1") +} + fn install_cargo_expand() -> Result<()> { execute_command( "cargo", diff --git a/frb_codegen/src/library/commands/command_runner.rs b/frb_codegen/src/library/commands/command_runner.rs index 16f795f1226..3fca2c934ab 100644 --- a/frb_codegen/src/library/commands/command_runner.rs +++ b/frb_codegen/src/library/commands/command_runner.rs @@ -38,7 +38,7 @@ macro_rules! command_run { }}; } -/// Formats a list of [`PathBuf`]s using the syntax detailed in [`run`]. +/// Formats a list of [`PathBuf`]s using the syntax detailed in [`command_run`]. #[doc(hidden)] #[macro_export] macro_rules! command_args { diff --git a/frb_codegen/src/library/utils/test_utils.rs b/frb_codegen/src/library/utils/test_utils.rs index a43c5230abe..5d7fe895d31 100644 --- a/frb_codegen/src/library/utils/test_utils.rs +++ b/frb_codegen/src/library/utils/test_utils.rs @@ -28,16 +28,28 @@ pub(crate) fn json_golden_test( let actual: Value = serde_json::from_str(&actual_str)?; debug!("json_golden_test sanitizers={sanitizers:?} actual:\n{actual_str}"); - raw_golden_test(actual, &actual_str, matcher_path, |x| { - Ok(serde_json::from_str(&x)?) - }) + raw_golden_test( + actual, + &actual_str, + matcher_path, + |x| Ok(serde_json::from_str(&x)?), + None, + ) } pub(crate) fn text_golden_test(actual: String, matcher_path: &Path) -> anyhow::Result<()> { - raw_golden_test(actual.clone(), &actual, matcher_path, |x| { + raw_golden_test( + actual.clone(), + &actual, + matcher_path, // Otherwise tests in macos/linux passes but fails on windows - Ok(x.replace("\r\n", "\n")) - }) + |x| Ok(x.replace("\r\n", "\n")), + Some(|expect, actual| { + #[cfg(test)] + use pretty_assertions::assert_str_eq as assert_eq; + assert_eq!(expect, actual); + }), + ) } fn raw_golden_test( @@ -45,11 +57,14 @@ fn raw_golden_test( actual_str: &str, matcher_path: &Path, deserializer: F, + asserter: Option, ) -> anyhow::Result<()> where T: Eq + Debug, F: Fn(String) -> anyhow::Result, { + #[cfg(test)] + use pretty_assertions::assert_eq; // This is *test* utils, not a part of real codegen, so no need to consider coverage // frb-coverage:ignore-start let expect = deserializer(if matcher_path.exists() { @@ -64,7 +79,8 @@ where fs::write(matcher_path, actual_str)?; } } else { - assert_eq!(actual, expect); + let asserter = asserter.unwrap_or(|expect, actual| assert_eq!(expect, actual)); + asserter(&expect, &actual); } Ok(()) diff --git a/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/Cargo.toml b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/Cargo.toml new file mode 100644 index 00000000000..c91a146840b --- /dev/null +++ b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "example" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +flutter_rust_bridge_macros.path = "../../../../../../../../frb_macros" + +[workspace] diff --git a/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/expect_output.dart b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/expect_output.dart new file mode 100644 index 00000000000..81ba392b73e --- /dev/null +++ b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/expect_output.dart @@ -0,0 +1,14 @@ + + // This file is automatically generated, so please do not edit it. +// Generated by `flutter_rust_bridge`@ {VERSION}. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import 'frb_generated.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + + + Future fnWithDefaultArg({int foo = 1, dynamic hint}) => RustLib.instance.api.fnWithDefaultArg(foo: foo, hint: hint); + + + \ No newline at end of file diff --git a/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/flutter_rust_bridge.yaml b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/flutter_rust_bridge.yaml new file mode 100644 index 00000000000..eb15b30ea2e --- /dev/null +++ b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/flutter_rust_bridge.yaml @@ -0,0 +1,3 @@ +rust_input: src/api.rs +dart_output: . +c_output: frb_generated.h diff --git a/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/pubspec.yaml b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/pubspec.yaml new file mode 100644 index 00000000000..14dd5aa3122 --- /dev/null +++ b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/pubspec.yaml @@ -0,0 +1 @@ +name: fake_dart_package diff --git a/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/src/api.rs b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/src/api.rs new file mode 100644 index 00000000000..290c9b26bfc --- /dev/null +++ b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/src/api.rs @@ -0,0 +1,4 @@ +#[flutter_rust_bridge_macros::frb] +pub fn fn_with_default_arg(#[frb(default = 1)] foo: i32) { + drop(foo); +} diff --git a/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/src/lib.rs b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/src/lib.rs new file mode 100644 index 00000000000..b32f9e29f1d --- /dev/null +++ b/frb_codegen/test_fixtures/library/codegen/generator/api_dart/mod/functions/src/lib.rs @@ -0,0 +1 @@ +mod api; diff --git a/frb_macros/src/lib.rs b/frb_macros/src/lib.rs index a055e6ba785..b9d4ff53996 100644 --- a/frb_macros/src/lib.rs +++ b/frb_macros/src/lib.rs @@ -10,10 +10,10 @@ use proc_macro::*; // frb-coverage:ignore-start #[proc_macro_attribute] pub fn frb(attribute: TokenStream, item: TokenStream) -> TokenStream { - let mut attribute = format_frb_attribute(format!("#[frb({attribute})]")); + let mut output = format_frb_attribute(format!("#[frb({attribute})]")); let item = strip_frb_attr(item); - attribute.extend(item); - attribute + output.extend(item); + output } fn strip_frb_attr(item: TokenStream) -> TokenStream { @@ -30,14 +30,16 @@ fn strip_frb_attr(item: TokenStream) -> TokenStream { Some(format_frb_attribute(format!("#[{}]", group.stream()))) } (_, T::Group(group)) => Some( - pound - .take() - .into_iter() - .chain(Some(T::Group(Group::new( + [ + pound.take(), + Some(T::Group(Group::new( group.delimiter(), strip_frb_attr(group.stream()), - )))) - .collect(), + ))), + ] + .into_iter() + .flatten() + .collect(), ), _ => Some(tok.into()), } diff --git a/justfile b/justfile index 765e5b0fff2..e91616d7431 100644 --- a/justfile +++ b/justfile @@ -27,5 +27,5 @@ _port_forward_ubuntu: ssh -L 8181:localhost:8181 ubuntu -N [no-cd] -expand *args: +_expand *args: cargo expand --config 'build.rustflags="--cfg frb_expand"' {{args}}