mozilla / uniffi-rs

a multi-language bindings generator for rust
https://mozilla.github.io/uniffi-rs/
Mozilla Public License 2.0
2.68k stars 219 forks source link

`unknown throw type` error when composing multiple crate #2225

Closed setoelkahfi closed 1 week ago

setoelkahfi commented 2 weeks ago

I have a networking crate that depends on a crate_error_codes crate and uses cargo swift to generate the swift package of the networking crate. I got an error while generating Swift binding with the cargo swift package command:

unknown throw type: Some(External { module_path: "crate_error_codes", name: "ErrorResponse", namespace: "crate_error_codes", kind: DataClass, tagged: false })

But I ran the same command directly from the crate_error_codes itself without issue. Below is my data structure:


#[derive(Serialize, Deserialize, Debug, Error)]
#[tsync]
pub enum ErrorResponse {
    Unknown { error_code: ErrorCode, message: String },
    UserNotFound { error_code: ErrorCode, message: String },
    // continues ...
}

impl Error for ErrorResponse {}

impl Display for ErrorResponse {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

#[repr(i32)]
#[derive(Serialize_repr, Deserialize_repr, Debug, EnumIter, Enum)]
#[tsync]
pub enum ErrorCode {
    Unknown = 0,
    //User-defined error codes start from 1000
    UserNotFound = 1000,
    // continues ....

setup_scaffolding!();

Cargo.toml

[package]
name = "crate_error_codes"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["lib", "cdylib", "staticlib"]
name = "crate_error_codes"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1"
tsync = "1"
strum = "0.26"
strum_macros = "0.26"
uniffi = "0.27"

[build-dependencies]
uniffi = { version = "0.27", features = ["build"] }
[package]
name = "crate_networking"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["lib", "staticlib"]
name = "crate_networking"

[dependencies]
reqwest = { version = "0.12", features = ["json"] }
uniffi = { version = "0.27", features = ["tokio"] }
log = "^0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
url-builder = "0.1.1"

# Local crates
crate_error_codes = { path = "./../../lib/crate_error_codes" }
crate_models = { path = "./../../lib/crate_models" }

[build-dependencies]
uniffi = { version = "0.27", features = ["build"] }

Any pointers?

mhammond commented 2 weeks ago

It sounds like uniffi_bindgen can't find metadata for your "crate_error_codes" - how exactly are you running the bindgen? In this scenario we'd expect you to be passing it a .a file which contains both crates.

(It looks like you might be using UDL too - in this scenario you will be unable to run the bindgen against one of the UDL files, as sharing types across crates requires "library mode")

setoelkahfi commented 2 weeks ago

I updated my issue description.

I don't use UDL. I mixed them previously and figured it would be simpler if I used the proc-macro entirely.

I ran the bindgen generation through the cargo swift package command. Could it be a cargo swift issue? The directory structure, though I doubt it matters, is:

lib
   - crate_error_codes
   - crate_networking

I got the error when running the cargo swift package from the crate_networking, not from the crate_error_codes.

mhammond commented 2 weeks ago

I'm afraid I don't know much about cargo swift, but in general, UniFFI will need to know about both crates when generating bindings. It's probably not possible to generate the bindings in 2 discrete steps, once for each crate.

setoelkahfi commented 2 weeks ago

I re-read the documentation and found an interesting point. This error happens if I use the proc-macro in the crate_error_codes. I got another error message when I used the UDL file in the crate_error_codes:

x Generating Swift bindings...

Failed due to the following error:
Could not generate UniFFI bindings for udl files due to the following error:
 No path known to UDL files for 'crate_error_codes'

This is what my crate_networking lib.rs file looks like:

// other uses
use crate_error_codes::{ErrorCode, ErrorResponse};
use uniffi::{self, export, setup_scaffolding, use_udl_enum, use_udl_error};

setup_scaffolding!();
use_udl_error!(crate_error_codes, ErrorResponse);
use_udl_enum!(crate_error_codes, ErrorCode);

#[export(async_runtime = "tokio")]
async fn content_home() -> Result<u64, ErrorResponse> {
    match carousel().await {
        Ok(_) => Ok(200),
        Err(_) => Err(ErrorResponse::Unknown {
            error_code: ErrorCode::Unknown,
            message: ErrorCode::Unknown.message(None).to_string(),
        }),
    }
}
mhammond commented 2 weeks ago

The problem in this case is that UniFFI knows there is a UDL in crate_error_codes, but it can't locate it. By default we use cargo_metadata to locate the source code of dependencies, but I'm not sure how cargo swift might work in that scenario, but it sounds similar to your other problem if somehow you are trying to build each crate individually or something?

For this specific issue, some debug prints in library_mode.rs might help.

setoelkahfi commented 1 week ago

I moved all my data models to the same crate with the networking layer. It sounds counterintuitive but works like a charm with cargo swift.