Crates.io | rsbind |
lib.rs | rsbind |
version | 0.6.0 |
source | src |
created_at | 2022-03-08 13:34:58.095768 |
updated_at | 2022-11-12 15:42:46.730771 |
description | Provide tools to bind rust trait with other language and export library artifact directly. Invoke rust functions just like you write it in native language. |
homepage | |
repository | |
max_upload_size | |
id | 546277 |
size | 1,002,490 |
Rsbind provide tools to bind rust trait with other language and export library artifact directly. It also help you invoke rust services just like you write it in native language.
It generate bindings from a Rust package and packaged to android aar or iOS lib or other library artifact. You don't need to write jni or other ffi code with this tool.
The tool may be useful for the people who want to use Rust as a cross-platform language and exporting multiple artifact for each platform.
Suppose you are writing two services in Rust and invoke from iOS and Android.
First you need write several Rust traits stands for your services.
pub trait Services: Send + Sync {
fn get_login_service() -> Box<dyn LoginService>;
fn get_upload_service() -> Box<dyn UploadService>;
}
pub trait LoginService: Send + Sync {
fn login(&self, user_name: String, pwd: String) -> Box<dyn Future>;
}
pub trait Future: Send + Sync {
fn get(&self) -> bool;
}
pub trait UploadService: Send + Sync {
fn upload(&self, path: String, listener: Box<dyn UploadProgress>) -> i64;
}
pub trait UploadProgress : Send + Sync {
fn on_progress(&self, id: i64, process: i64, total: i64);
}
Then you can implement your trait to achive your services.
pub struct ServiceHolder {}
impl Services for ServiceHolder {
fn get_login_service() -> Box<dyn LoginService> {
Box::new(LoginServiceImp {})
}
fn get_upload_service() -> Box<dyn UploadService> {
Box::new(UploadServiceImp {})
}
}
pub struct LoginServiceImp {}
impl LoginService for LoginServiceImp {
fn login(&self, user_name: String, pwd: String) -> Box<dyn Future> {
struct FutureImp {
pub user_name: String,
pub pwd: String,
}
impl Future for FutureImp {
fn get(&self) -> bool {
let handle = thread::spawn(move || {
// do your login
true
});
handle.join().unwrap()
}
}
Box::new(FutureImp { user_name, pwd })
}
}
pub struct UploadServiceImp {}
impl UploadService for UploadServiceImp {
fn upload(&self, path: String, listener: Box<dyn UploadProgress>) -> i64 {
thread::spawn(move || {
// doing uploading
listener.on_progress(99999, 10, 12345);
});
99999
}
}
After that, run rsbind command to generate iOS and Android library artifact.
rsbind . android all
rsbind . ios all
Then with iOS library, you can invoke service from swift directly.
let loginService = RustLib.newServices().getLoginService();
let future = loginService.login(user_name: "sidney.wang", pwd: "88888888")
let result = future.get();
print("login result = \(result)")
class Listener : UploadProgress {
func onProgress(id: Int64, process: Int64, total: Int64) {
print("Progress is \(process)/\(total)")
}
}
let uploadService = RustLib.newServices().getUploadService();
uploadService.upload(path: "to/your/path", listener: Listener())
In Android, it is very similar, just run java code.
LoginService loginService = RustLib.newServices().getLoginService();
Future future = loginService.login("sidney.wang", "88888888");
boolean result = future.get();
Log.i(TAG, "login result is " + result);
UploadService uploadService = RustLib.newServices().getUploadService();
uploadService.upload("to/your/path", new UploadProgress() {
@Override
public void onProgress(long id, long process, long total) {
Log.i(TAG, "upload process is " + process);
}
});
cargo install --git https://github.com/rs-bind/rsbind.git --force -- rsbind
Maybe you want to split your api and implementation, then you can use these two structure.
You can put your interface to contract module and implemation to imp module. Expose these two modules in lib.rs.
// such as your code in contract dir as below:
pub trait YourContract : Send + Sync {
fn test_simple(arg1: i32, arg2: String) -> String;
fn test_callback(arg: Box<dyn Callback>);
fn test_struct() -> StructSimple;
fn test_return_callback() -> Box<dyn Callback>;
}
pub trait Callback : Send + Sync {
fn on_callback(&self, arg1: i64, arg2: String);
}
pub struct StructSimple {
pub arg3: String,
pub arg4: bool,
}
// Your implementation is as below
pub struct YourImplemetation {}
impl YourContract for YourImplemetation {
fn test_simple(arg1: i32, arg2: String) -> String {
format!("Your test_simple result is {}_{}", arg1, arg2)
}
fn test_callback(arg: Box<dyn Callback>) {
arg.on_callback(123i64, "hello callback".to_owned());
}
fn test_struct() -> StructSimple {
StructSimple {
arg1: "struct".to_owned(),
arg2: true
}
}
fn test_return_callback() -> Box<dyn Callback> {
struct Instance{}
impl Callback for Instance {
fn on_callback(&self, arg1: i64, arg2: String) {
}
}
Box::new(Instance{})
}
}
Rsbind usage:
rsbind path-of-project android/ios/mac/jar/all ast/bridge/artifact/header/build/all
YourContract instance = RustLib.newYourContract();
instance.testCallback(new Callback(){
void onCallback(long arg1, String arg2) {
// do your things.
}
})
Swift is very similar.
You can create a file named Rsbind.toml to add some configuration.
[android]
rustc_param = ""
arch = ["armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android"]
release = true
namespace = "com.afoxer.xxx.ffi"
so_name = "demo"
ext_lib = []
features_def = ["xxxx=[]"]
[ios]
rustc_param = ""
arch = ["armv7-apple-ios", "i386-apple-ios", "x86_64-apple-ios"]
release = true
features_def = []
[mac]
rustc_param = ""
release = true
features_def = []
[jar]
rustc_param = ""
release = true
namespace = "com.afoxer.xxx.ffi"
so_name = "demo"
#ext_lib = []
#features_def = ["xxxx=[]"]
supported types in Callback:
Will support it in near future.
It is different to define a callback and a normal trait. It should contains &self in every callback but not in normal trait.
Callback:
pub trait Callback : Send + Sync {
fn on_callback(&self, arg1: i32, arg2: String, arg3: bool, arg4: f32, arg5: f64) -> i32;
fn on_callback2(&self, arg1: bool) -> bool;
fn on_callback_complex(&self, arg1: StructSimple) -> bool;
fn on_callback_arg_vec(&self, arg1: Vec<StructSimple>) -> bool;
fn on_callback_arg_vec_simple(&self, arg1: Vec<String>) -> bool;
}
Normal trait:
pub trait TestContract1 : Send + Sync {
fn test_arg_vec(arg: Vec<String>) -> i32;
fn test_return_vec(arg: u8) -> Vec<i32>;
fn test_arg_callback(arg: Box<dyn Callback>) -> u8;
fn test_bool(arg1: bool) -> bool;
fn test_struct() -> StructSimple;
fn test_struct_vec() -> Vec<StructSimple>;
}