fzyzcjy / flutter_rust_bridge

Flutter/Dart <-> Rust binding generator, feature-rich, but seamless and simple.
https://fzyzcjy.github.io/flutter_rust_bridge/
MIT License
4.14k stars 285 forks source link

Add Rust Ipv4Addr, Ipv6Addr to Dart InternetAddressType class #2212

Closed VladTheJunior closed 1 week ago

VladTheJunior commented 1 month ago

I found, that there is no default rust to dart conversion for Network types Such as Ipv4Addr, Ipv6Addr to dart InternetAddressType since frb converts it to opaque struct. Can you add this conversion by default in frb? Rust IpAddr Dart InternetAddressType

fzyzcjy commented 1 month ago

That sounds reasonable. Currently, you can utilize https://cjycode.com/flutter_rust_bridge/guides/types/translatable/custom and add a dozen of lines in your code to support that conversion.

VladTheJunior commented 1 month ago

I tried to add custom encoder but it fails now, because in frb_generated.rs there are 2 encoders for Ipv4Address:

impl SseEncode for Ipv4Addr {
    // Codec=Sse (Serialization based), see doc to use other codecs
    fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
        <RustOpaqueNom<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Ipv4Addr>>>::sse_encode(flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, StdArc<_>>(self), serializer);
    }
}
...
impl SseEncode for Ipv4Addr {
    // Codec=Sse (Serialization based), see doc to use other codecs
    fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
        <String>::sse_encode(crate::api::device::encode_ipv4_type(self), serializer);
    }
}

my custom encoder

#[frb(rust2dart(
    dart_type = "InternetAddress",
    dart_code = "InternetAddress.fromRawAddress({})"
))]
pub fn encode_ipv4_type(raw: Ipv4Addr) -> Vec<u8> {
    return raw.octets().to_vec();
}

#[frb(dart2rust(dart_type = "InternetAddress", dart_code = "{}.address"))]
pub fn decode_ipv4_type(raw: String) -> Ipv4Addr {
    raw.parse().unwrap()
}
VladTheJunior commented 1 month ago

I tried to create sample for you, but it has another error, idk how to fix it

  SEVERE: error[E0599]: no method named `into_into_dart` found for struct `Ipv4Addr` in the current scope
  SEVERE: error[E0277]: the trait bound `*mut wire_cst_list_prim_u_8_strict: frb_generated::CstDecode<Ipv4Addr>` is not satisfied
SEVERE : error : could not compile `rust_lib_test` (lib) due to 2 previous errors [C:\Users\vladt\Desktop\test\build\windows\x64\plugins\rust_lib_test\rust_lib_test_cargokit.vcxproj]

https://github.com/VladTheJunior/test_frb

fzyzcjy commented 1 month ago

Hmm, firstly maybe try to let the encode and decode have the same time (currently it is Vec and String in the two scenarios)

In addition, does the demo in the doc work? If so maybe we can try to mimic that demo.

Another possibility is that, maybe Ipv4Addr is used as both opaque and non-opaque, which confuses frb.

VladTheJunior commented 1 month ago

Hmm, firstly maybe try to let the encode and decode have the same time (currently it is Vec and String in the two scenarios)

I used it in test example in my previous comment

Another possibility is that, maybe Ipv4Addr is used as both opaque and non-opaque, which confuses frb.

How can I check or fix that

VladTheJunior commented 1 month ago

Okay, I will add some summary. After create new frb project, I added these lines to simple.rs file:

use std::net::Ipv4Addr;

use flutter_rust_bridge::frb;

#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
pub fn greet(name: Ipv4Addr) -> String {
    format!("Hello, {}!", name.to_string())
}

#[frb(rust2dart(
    dart_type = "InternetAddress",
    dart_code = "InternetAddress({})"
))]
pub fn encode_ipv4_type(raw: Ipv4Addr) -> String {
    return raw.to_string();
}

#[frb(dart2rust(dart_type = "InternetAddress", dart_code = "{}.address"))]
pub fn decode_ipv4_type(raw: String) -> Ipv4Addr {
    raw.parse().unwrap()
}

and this to flutter_rust_bridge.yaml:

rust_preamble: use std::net::Ipv4Addr;
dart_preamble: import 'dart:io'; 

and in Dart:

greet(name: InternetAddress("10.0.11.1"))

All of this works perfectly. But when I add struct Device with Ipv4Addr field it starts to generate error no method named `into_into_dart` found for struct `Ipv4Addr` in the current scope

#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
pub fn greet(name: Device) -> String {
    format!("Hello, {}!", name.ip.to_string())
}

pub struct Device{
    pub ip: Ipv4Addr
}
greet(name: Device(ip: InternetAddress("10.0.11.1")))
VladTheJunior commented 1 month ago

Idk why, but I figured out why it thinks about opaque type. When I add Debug derive to Device struct it becomes autoopaque:

#[derive(Debug)]
pub struct Device{
    pub ip: Ipv4Addr
}
fzyzcjy commented 1 month ago

But when I add struct Device with Ipv4Addr field it starts to generate error

Also try full_dep: false in case you are using the dco codec which is not supoorted yet

VladTheJunior commented 1 month ago

Also try full_dep: false in case you are using the dco codec which is not supoorted yet

I thought full_dep is false by default. When I added full_dep: false, with enabled Derive(Debug) it stops generate my custom encoders, only autoopaque one.

When Derive(Debug) is removed it still generate error no method named `into_into_dart` found for struct `Ipv4Addr` in the current scope

I updated me test example in this repo: https://github.com/VladTheJunior/test_frb

VladTheJunior commented 1 month ago

Why into_into_dart is not auto generated used by my custom encoder/decoder?

fzyzcjy commented 1 month ago

A workaround about the into_into_dart thing before it is fixed/implemented may be that, implement the into_into_dart manually (which only takes a few lines). Another temporary workaround is to change the type used.

VladTheJunior commented 1 month ago

implement the into_into_dart manually

What is correct way to do it? I tried

impl flutter_rust_bridge::IntoIntoDart<std::net::Ipv4Addr> for std::net::Ipv4Addr {
    fn into_into_dart(self) -> std::net::Ipv4Addr {
        self
    }
}

but got this:

SEVERE: error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
SEVERE: error[E0277]: the trait bound `allo_isolate::ffi::DartCObject: From<Ipv4Addr>` is not satisfied
fzyzcjy commented 1 month ago

Maybe mimic how a third party type (via mirroring) is implemented.

VladTheJunior commented 1 month ago

I think it's better to wait fixes/adding Ipv4 by default in frb. Thank you for all responses and your big work on this project

fzyzcjy commented 1 month ago

Looks reasonable, and you are welcome

fzyzcjy commented 1 week ago

I cannot reproduce the into_into_dart problem. The following code compiles without any issue:

pub fn greet(name: Ipv4Addr) -> String {
    unimplemented!()
}

#[frb(rust2dart(dart_type = "InternetAddress", dart_code = "InternetAddress({})"))]
pub fn encode_ipv4_type(raw: Ipv4Addr) -> String {
    return raw.to_string();
}

#[frb(dart2rust(dart_type = "InternetAddress", dart_code = "{}.address"))]
pub fn decode_ipv4_type(raw: String) -> Ipv4Addr {
    raw.parse().unwrap()
}

On the other hand, for fields inside structs, if a field has custom serialization type, it is not understood. I will fix that now.

EDIT: After fixing that one, the into_into_dart issue finally reproduces. Fix it as well.