-
Notifications
You must be signed in to change notification settings - Fork 232
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Foreign-implemented async trait methods (#2017)
These methods input a completion function that they call when the async function is complete. They return a ForeignFuture struct, which represents the foreign task object and is used to drop/cancel futures. Like Rust, dropping and cancelling are coupled together into one operation. - Added `RustCallStatus` as an `FfiType` variant, since I wanted to use it in the `ForeignFutureResult` structs. In theory, we could define `RustCallStatus` a `FfiType::Struct`, but I didn't want to introduce that change in this PR. - Fixed the result mapping code to work with async functions. Before we were executing the mapping call, then awaiting the result, but we need to do that in the opposite order (`foo.into().await` vs `foo.await.into()`). Also, specify the return type generics for `rust_future_new` so that the Rust can figure out the into() generics.
- Loading branch information
Showing
46 changed files
with
1,615 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "uniffi-example-async-api-client" | ||
edition = "2021" | ||
version = "0.26.1" | ||
license = "MPL-2.0" | ||
publish = false | ||
|
||
[lib] | ||
crate-type = ["lib", "cdylib"] | ||
name = "uniffi_async_api_client" | ||
|
||
[dependencies] | ||
async-trait = "0.1" | ||
uniffi = { workspace = true } | ||
serde = { version = "1", features=["derive"] } | ||
serde_json = "1" | ||
thiserror = "1.0" | ||
|
||
[build-dependencies] | ||
uniffi = { workspace = true, features = ["build"] } | ||
|
||
[dev-dependencies] | ||
uniffi = { workspace = true, features = ["bindgen-tests"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
fn main() { | ||
uniffi::generate_scaffolding("src/async-api-client.udl").unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
namespace async_api_client { | ||
string test_response_data(); | ||
}; | ||
|
||
[Error] | ||
interface ApiError { | ||
Http(string reason); | ||
Api(string reason); | ||
Json(string reason); | ||
}; | ||
|
||
// Implemented by the foreign bindings | ||
[Trait, WithForeign] | ||
interface HttpClient { | ||
[Throws=ApiError, Async] | ||
string fetch(string url); // fetch an URL and return the body | ||
}; | ||
|
||
dictionary Issue { | ||
string url; | ||
string title; | ||
IssueState state; | ||
}; | ||
|
||
enum IssueState { | ||
"Open", | ||
"Closed", | ||
}; | ||
|
||
// Implemented by the Rust code | ||
interface ApiClient { | ||
constructor(HttpClient http_client); | ||
|
||
[Throws=ApiError, Async] | ||
Issue get_issue(string owner, string repository, u32 issue_number); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
use std::sync::Arc; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum ApiError { | ||
#[error("HttpError: {reason}")] | ||
Http { reason: String }, | ||
#[error("ApiError: {reason}")] | ||
Api { reason: String }, | ||
#[error("JsonError: {reason}")] | ||
Json { reason: String }, | ||
} | ||
|
||
pub type Result<T> = std::result::Result<T, ApiError>; | ||
|
||
#[async_trait::async_trait] | ||
pub trait HttpClient: Send + Sync { | ||
async fn fetch(&self, url: String) -> Result<String>; | ||
} | ||
|
||
#[derive(Debug, serde::Deserialize)] | ||
pub struct Issue { | ||
url: String, | ||
title: String, | ||
state: IssueState, | ||
} | ||
|
||
#[derive(Debug, serde::Deserialize)] | ||
pub enum IssueState { | ||
#[serde(rename = "open")] | ||
Open, | ||
#[serde(rename = "closed")] | ||
Closed, | ||
} | ||
|
||
pub struct ApiClient { | ||
http_client: Arc<dyn HttpClient>, | ||
} | ||
|
||
impl ApiClient { | ||
pub fn new(http_client: Arc<dyn HttpClient>) -> Self { | ||
Self { http_client } | ||
} | ||
|
||
pub async fn get_issue( | ||
&self, | ||
owner: String, | ||
repository: String, | ||
issue_number: u32, | ||
) -> Result<Issue> { | ||
let url = | ||
format!("https://api.github.com/repos/{owner}/{repository}/issues/{issue_number}"); | ||
let body = self.http_client.fetch(url).await?; | ||
Ok(serde_json::from_str(&body)?) | ||
} | ||
} | ||
|
||
impl From<serde_json::Error> for ApiError { | ||
fn from(e: serde_json::Error) -> Self { | ||
Self::Json { | ||
reason: e.to_string(), | ||
} | ||
} | ||
} | ||
|
||
/// Sample data downloaded from a real github api call | ||
/// | ||
/// The tests don't make real HTTP calls to avoid them failing because of network errors. | ||
pub fn test_response_data() -> String { | ||
String::from( | ||
r#"{ | ||
"url": "https://api.github.com/repos/mozilla/uniffi-rs/issues/2017", | ||
"repository_url": "https://api.github.com/repos/mozilla/uniffi-rs", | ||
"labels_url": "https://api.github.com/repos/mozilla/uniffi-rs/issues/2017/labels{/name}", | ||
"comments_url": "https://api.github.com/repos/mozilla/uniffi-rs/issues/2017/comments", | ||
"events_url": "https://api.github.com/repos/mozilla/uniffi-rs/issues/2017/events", | ||
"html_url": "https://github.com/mozilla/uniffi-rs/issues/2017", | ||
"id": 2174982360, | ||
"node_id": "I_kwDOECpYAM6Bo5jY", | ||
"number": 2017, | ||
"title": "Foreign-implemented async traits", | ||
"user": { | ||
"login": "bendk", | ||
"id": 1012809, | ||
"node_id": "MDQ6VXNlcjEwMTI4MDk=", | ||
"avatar_url": "https://avatars.githubusercontent.com/u/1012809?v=4", | ||
"gravatar_id": "", | ||
"url": "https://api.github.com/users/bendk", | ||
"html_url": "https://github.com/bendk", | ||
"followers_url": "https://api.github.com/users/bendk/followers", | ||
"following_url": "https://api.github.com/users/bendk/following{/other_user}", | ||
"gists_url": "https://api.github.com/users/bendk/gists{/gist_id}", | ||
"starred_url": "https://api.github.com/users/bendk/starred{/owner}{/repo}", | ||
"subscriptions_url": "https://api.github.com/users/bendk/subscriptions", | ||
"organizations_url": "https://api.github.com/users/bendk/orgs", | ||
"repos_url": "https://api.github.com/users/bendk/repos", | ||
"events_url": "https://api.github.com/users/bendk/events{/privacy}", | ||
"received_events_url": "https://api.github.com/users/bendk/received_events", | ||
"type": "User", | ||
"site_admin": false | ||
}, | ||
"labels": [ | ||
], | ||
"state": "open", | ||
"locked": false, | ||
"assignee": null, | ||
"assignees": [ | ||
], | ||
"milestone": null, | ||
"comments": 0, | ||
"created_at": "2024-03-07T23:07:29Z", | ||
"updated_at": "2024-03-07T23:07:29Z", | ||
"closed_at": null, | ||
"author_association": "CONTRIBUTOR", | ||
"active_lock_reason": null, | ||
"body": "We currently allow Rust code to implement async trait methods, but foreign implementations are not supported. We should extend support to allow for foreign code.\\r\\n\\r\\nI think this is a key feature for full async support. It allows Rust code to define an async method that depends on a foreign async method. This allows users to use async code without running a Rust async runtime, you can effectively piggyback on the foreign async runtime.", | ||
"closed_by": null, | ||
"reactions": { | ||
"url": "https://api.github.com/repos/mozilla/uniffi-rs/issues/2017/reactions", | ||
"total_count": 0, | ||
"+1": 0, | ||
"-1": 0, | ||
"laugh": 0, | ||
"hooray": 0, | ||
"confused": 0, | ||
"heart": 0, | ||
"rocket": 0, | ||
"eyes": 0 | ||
}, | ||
"timeline_url": "https://api.github.com/repos/mozilla/uniffi-rs/issues/2017/timeline", | ||
"performed_via_github_app": null, | ||
"state_reason": null | ||
}"#, | ||
) | ||
} | ||
|
||
uniffi::include_scaffolding!("async-api-client"); |
Oops, something went wrong.