Membrane is an opinionated crate that generates a Dart package from a Rust library. Extremely fast performance with strict typing and zero copy returns over the FFI boundary via bincode.

Related tags

Utilities membrane
Overview

Membrane

Membrane is an opinionated crate that generates a Dart package from a Rust library. Extremely fast performance with strict typing and zero copy returns over the FFI boundary via bincode.

Example

First create a lib.rs that exposes a RUNTIME static that will survive for the lifetime of the program. RUNTIME must provide a tokio style spawn function:

use once_cell::sync::Lazy;
use tokio::runtime::{Builder, Runtime};

pub(crate) static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
  Builder::new_multi_thread()
    .worker_threads(2)
    .thread_name("libexample")
    .build()
    .unwrap()
});

Then write some code that is annotated with the #[async_dart] macro. The functions can be anywhere in your program and may return either an async Result or a Stream > :

impl Stream > { futures::stream::iter(vec![Ok(Default::default())]) } #[async_dart(namespace = "accounts")] pub async fn contact(id: String) -> Result { Ok(data::Contact { id: id.parse().unwrap(), ..Default::default() }) } ">
use membrane::async_dart;
use tokio_stream::Stream;

use crate::data;

#[async_dart(namespace = "accounts")]
pub fn contacts() -> impl Stream
     Result
     
      > {
  futures
      ::stream
      ::
      iter(
      vec![
      Ok(
      Default
      ::
      default())])
}

#[async_dart(namespace = 
      "accounts")]

      pub 
      async 
      fn 
      contact(id: 
      String) -> 
      Result
      
        {
  
       Ok(data
       ::Contact {
    id: id.
       parse().
       unwrap(),
    ..
       Default
       ::
       default()
  })
}
      
     
    

And now you are ready to generate the Dart package. Note that this goes in a bin/generator.rs or similar to be ran with cargo run rather than in a build.rs (which only runs before compilation):

fn main() {
  // if nothing else in this generator.rs references lib.rs then
  // at least call a dummy function so lib.rs doesn't get optimized away
  example::load();

  let mut project = membrane::Membrane::new();
  project
    // name the output pub package
    .package_destination_dir("../dart_example")
    // give the name of the .so or .dylib that your Rust program provides
    .using_lib("libexample")
    .create_pub_package()
    .write_api()
    .write_c_headers()
    .write_bindings();
}

If everything went as planned you can now call Rust from Dart with:

cd example &&
cargo build &&
cd ../dart_example &&
cp ../example/target/debug/libexample.dylib . &&
dart --enable-asserts run

(--enable-asserts enables a pretty print toString() in the generated classes)

import 'package:dart_example/accounts.dart';

void main(List<String> arguments) async {
  var accounts = AccountsApi();
  print(await accounts.contact(id: "1"));
}
Comments
  • Fix/example

    Fix/example

    Hello again! I could correctly build the plugin so far, then I tried to run the tests in the generated dart_example folder but there was a few minor obstacles:

    • test dependency was missing in dart_example/pubspec.yml

    • fixed dart_example/test/enum_test.dart

    • for a reason that I ignore I had to build with crate type dylib

    • manually copy built Rust library to ../dart_example/libexample.dylib

      probably because on M1 I'm required to run:

      export MEMBRANE_LLVM_PATHS=/usr/local/opt/llvm && cargo run --target=x86_64-apple-darwin
      

      which builds to example/target/x86_64-apple-darwin/debug/libexample.dylib instead.

      Library is called libexample.dylib, as per generator lib arg while some binaries are called generator / generator.d after Cargo bin name honestly I'm not sure if it matters, but in case.

    Now, it runs smooth :)

    opened by Roms1383 4
  • Update ffigen

    Update ffigen

    Help me Jerel, you're my only hope.

    Save me Jerel

    I've gone down a bit of a rabbit hole trying to get M1 support without resorting to hacks like symlinking the Homebrew llvm. Latest ffigen works great on the M1, but we've got some membrane type issues to iron out.

    Tests are allllllmost passing, as seen in https://github.com/jerel/membrane/commit/35b8da1e8982805a1ed91c487a96f9e896617e80

    Without the test in that commit skipped, the first error we get is (https://github.com/jerel/membrane/commit/1ea6cdd9b03ee9f7602adb95c9ab0eb3463251f7)

    00:00 +6 -1: can call a function with optional args with none of the args or all of the args [E]
      type 'Pointer<Int64>' is not a subtype of type 'Pointer<Long>' of 'two'
      package:dart_example/accounts.dart 1928:31  AccountsApi.optionsDemo
      test/main_test.dart 79:24                   main.<fn>
    

    Okay. Easy enough, right? Change Int64 to Long (https://github.com/jerel/membrane/commit/3219c7f9d5e8f4e81f0953a2818d39242897a205).

    Now the fun error happens.

    00:00 +0 -1: loading test/main_test.dart [E]
      Failed to load "test/main_test.dart":
      lib/accounts.dart:1887:11: Error: The method 'asTypedList' isn't defined for the class 'Pointer<Long>'.
       - 'Pointer' is from 'dart:ffi'.
       - 'Long' is from 'dart:ffi'.
      Try correcting the name to the name of an existing method, or defining a method named 'asTypedList'.
            ptr.asTypedList(1).setAll(0, [two]);
                ^^^^^^^^^^^
    

    Digging in, it looks like there is no extension LongPointer on Pointer<Long> like there is for extension Int64Pointer on Pointer<Int64> and most of the other types. We can see where they recently added the extension BoolPointer during some native type work.

    Do we need a feature request to the Dart SDK? And then we will wait for another language release?

    I'm not entirely sure why we need the asTypedList part. Is there a way we can work around the lack of it?

    Potential other solution is finding some way to get the bindings to generate with Int64 instead of Long for Rust i64.

    opened by kturney 3
  • Deserialization error when passing rust variant enums

    Deserialization error when passing rust variant enums

    With Membrane, there seems to be a deserialization issue on the dart side when passing rust variant enums:

    #[dart_enum(namespace = "ast")]
    #[derive(Debug, Clone, Deserialize, Serialize)]
    pub enum Stmt {
        ExpStmt(Expr),
    }
    

    Error:

    Unhandled exception:
    Exception: Unknown variant index for Expr: 56542
    
    opened by jc324 3
  • Ready for production use?

    Ready for production use?

    Hi, first of all congrats on this amazing project!

    I'm starting a new Flutter app backed by a Rust lib and I'd like to hear your opinion if you think this is ready for "production". In addition, do you foresee many breaking/incompatible changes with previous versions as you keep evolving your project? Thanks!

    opened by xD0135 2
  • Yocto build error

    Yocto build error

    Moving to tip of tree I'm hitting this build error:

    |      Running `rustc --crate-name serde_bytes --edition=2018 /__w/meta-flutter/yocto-kirkstone/build/tmp/work/core2-64-poky-linux/membrane-dart-example/git-r0/cargo_home/bitbake/serde_bytes-0.11.5/src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --crate-type lib --emit=dep-info,metadata,link -C opt-level=3 -C embed-bitcode=no --cfg 'feature="default"' --cfg 'feature="std"' -C metadata=53606f82d515ffee -C extra-filename=-53606f82d515ffee --out-dir /__w/meta-flutter/yocto-kirkstone/build/tmp/work/core2-64-poky-linux/membrane-dart-example/git-r0/build/target/x86_64-poky-linux/release/deps --target x86_64-poky-linux -C linker=/__w/meta-flutter/yocto-kirkstone/build/tmp/work/core2-64-poky-linux/membrane-dart-example/git-r0/wrapper/target-rust-ccld -L dependency=/__w/meta-flutter/yocto-kirkstone/build/tmp/work/core2-64-poky-linux/membrane-dart-example/git-r0/build/target/x86_64-poky-linux/release/deps -L dependency=/__w/meta-flutter/yocto-kirkstone/build/tmp/work/core2-64-poky-linux/membrane-dart-example/git-r0/build/target/release/deps --extern serde=/__w/meta-flutter/yocto-kirkstone/build/tmp/work/core2-64-poky-linux/membrane-dart-example/git-r0/build/target/x86_64-poky-linux/release/deps/libserde-8c571d51b5658631.rmeta --cap-lints allow -L /__w/meta-flutter/yocto-kirkstone/build/tmp/work/core2-64-poky-linux/membrane-dart-example/git-r0/recipe-sysroot/usr/lib/rust --remap-path-prefix=/__w/meta-flutter/yocto-kirkstone/build/tmp/work/core2-64-poky-linux/membrane-dart-example/git-r0=/usr/src/debug/membrane-dart-example/git-r0`
    [1181](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1182)
    | error[E0107]: this struct takes 3 generic arguments but 2 generic arguments were supplied
    [1182](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1183)
    |   --> /usr/src/debug/membrane-dart-example/git-r0/cargo_home/bitbake/serde_yaml-0.8.21/src/mapping.rs:16:10
    [1183](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1184)
    |    |
    [1184](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1185)
    | 16 |     map: IndexMap<Value, Value>,
    [1185](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1186)
    |    |          ^^^^^^^^ -----  ----- supplied 2 generic arguments
    [1186](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1187)
    |    |          |
    [1187](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1188)
    |    |          expected 3 generic arguments
    [1188](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1189)
    |    |
    [1189](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1190)
    | help: add missing generic argument
    [1190](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1191)
    |    |
    [1191](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1192)
    | 16 |     map: IndexMap<Value, Value, S>,
    [1192](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1193)
    |    |                               +++
    [1193](https://github.com/meta-flutter/meta-flutter/runs/6757718596?check_suite_focus=true#step:9:1194)
    |
    
    opened by jwinarske 2
  • Ubuntu Linker optimization fix

    Ubuntu Linker optimization fix

    -prevents membrane_cancel_membrane_task and membrane_free_membrane_vec from being optimized out on Ubuntu

    Signed-off-by: Joel Winarske [email protected]

    opened by jwinarske 1
  • Add support for both native Dart enums and class enums at the same time

    Add support for both native Dart enums and class enums at the same time

    Previously a project would generate all classes if project.with_c_style_enums(false) was set or all Dart enums if it was true. This change makes project.with_c_style_enums(true) (the default) behave such that it generates Dart enums for simple Rust enums and generates classes for Rust enums that contain data.

    opened by jerel 1
  • Add tests and some missing error messages for function arg types

    Add tests and some missing error messages for function arg types

    A few types, such as Vec<i32> when used as a Rust function argument, weren't resulting in a helpful error message. This adds tests and support for that as well as adding support for the types u64, u128, and i128 as function arguments.

    opened by jerel 0
  • Upgrade serde-generate to support Dart enum getters

    Upgrade serde-generate to support Dart enum getters

    This upgrade adds a copyWith implementation (which handles null) like so:

      Contact copyWith({
        int? id,
        String? fullName,
        Status? status,
      }) {
        return Contact(
          id: id ?? this.id,
          fullName: fullName ?? this.fullName,
          status: status ?? this.status,
        );
      }
    

    along with adding getters in the base class for common fields of data enums.

    opened by jerel 0
  • Add borrowing

    Add borrowing

    This provides the ability to borrow Dart types from another namespace instead of generating a separate copy into each namespace. Example:

    #[async_dart(
      namespace = "orgs",
      borrow = "locations::Location",
      borrow = "accounts::Contact",
      borrow = "accounts::Filter",
      borrow = "accounts::Match",
      borrow = "accounts::Status"
    )]
    pub async fn get_org_with_borrowed_type(id: data::Filter) -> Result<data::Organization, String> {
      let _ = id;
      Ok(data::Organization {
        id: 1,
        owner: data::Contact::default(),
        location: data::Location {
          polyline_coords: vec![(-104.0185546875, 43.004647127794435)],
        },
      })
    }
    

    The borrow format is namespace::Class.

    opened by jerel 0
  • Add basic web support to allow a project to compile to javascript

    Add basic web support to allow a project to compile to javascript

    While we don't currently support connections over the network this PR adds compilation support so that projects which consume membrane generated code can target the web platform themselves without build-time errors.

    opened by jerel 0
  • Windows Support

    Windows Support

    Hi,

    Cool library. I would like to suggest windows support, that would be really helpful. So far, I'm just manually patching loader.dart with this:

    if (Platform.isWindows) {
      Logger('membrane').info('Opening native library example.dll');
      return DynamicLibrary.open('example.dll');
    }
    

    Thanks.

    opened by jc324 6
Owner
Jerel Unruh
Building connected car tech at Toyota Connected.
Jerel Unruh
This library provides a stable polyfill for Rust's Strict Provenance experiment.

This library provides a stable polyfill for Rust's Strict Provenance experiment.

Aria Beingessner 57 Nov 15, 2022
Simple procedural macros `tnconst![...]`, `pconst![...]`, `nconst![...]` and `uconst![...]` that returns the type level integer from `typenum` crate.

typenum-consts Procedural macros that take a literal integer (or the result of an evaluation of simple mathematical expressions or an environment vari

Jim Chng 3 Mar 30, 2024
Extremely fast JavaScript minifier, available for Rust and Node.js

minify-js Extremely fast JavaScript minifier, written in Rust. Goals Fully written in Rust for maximum compatibility with Rust programs and derivative

Wilson Lin 78 Jul 13, 2023
Hide the X11 mouse pointer while you're typing.

rxbanish: let's play hide the pointer rxbanish is a simple program to hide the X11 mouse pointer while you're typing. It's inspired by the C program x

Cliff L. Biffle 8 Apr 7, 2024
A lightning fast version of tmux-fingers written in Rust, copy/pasting tmux like vimium/vimperator

tmux-thumbs A lightning fast version of tmux-fingers written in Rust for copy pasting with vimium/vimperator like hints. Usage Press ( prefix + Space

Ferran Basora 598 Jan 2, 2023
An opinionated, practical color management library for games and graphics.

colstodian An opinionated color management library built on top of kolor. Introduction colstodian is a practical color management library for games an

Gray Olson 27 Dec 7, 2022
Experimental Rust tool for generating FFI definitions allowing many other languages to call Rust code

Diplomat is an experimental Rust tool for generating FFI definitions allowing many other languages to call Rust code. With Diplomat, you can simply define Rust APIs to be exposed over FFI and get high-level C, C++, and JavaScript bindings automatically!

null 255 Dec 30, 2022
Simplified glue code generation for Deno FFI libraries written in Rust.

deno_bindgen This tool aims to simplify glue code generation for Deno FFI libraries written in Rust. Quickstart # install CLI deno install -Afq -n den

Divy Srivastava 173 Dec 17, 2022
An opinionated Rust library for interacting with AWS DynamoDB single-table designs.

Modyne An opinionated library for interacting with AWS DynamoDB single-table designs. † Motive Modyne follows the precepts laid out for effective sing

Marcus Griep 14 Jun 8, 2023
A tool that generates a Sublime Text project file that helps you get started using Scoggle.

README A tool that generates a Sublime Text project file that helps you get started using Scoggle. While Scoggle-Gen may not find every single source

Sanjiv Sahayam 0 Jan 10, 2022
syncmap is a fast, concurrent cache library built with a focus on performance and correctness.

syncmap syncmap syncmap is a fast, concurrent cache library syncmap is a fast, concurrent cache library built with a focus on performance and correctn

Behrouz R.Farsi 15 Dec 2, 2022
📮 load, write, and copy remote and local assets

axoasset This library offers read, write, and copy functions, for local and remote assets given a string that contains a relative or absolute local pa

axo 7 Jan 25, 2023
Lintje is an opinionated linter for Git.

Lintje Lintje is an opinionated linter for Git. It lints commit messages based on a preconfigured set of rules focussed on promoting communication bet

Tom de Bruijn 26 Dec 18, 2022
Bongo Copy Cat wants to be involved in everything you do but instead just imitates you hitting your keyboard all day. After all it's just a cat.

Bongo Copy Cat Introduction Bongo Copy Cat wants to be involved in everything you do but instead just imitates you hitting your keyboard all day. Afte

Abhijeet Singh 4 Jan 23, 2023
One copy of Electron to rule them all.

chroma One copy of Electron to rule them all. chroma keeps a central, up-to-date version of Electron, and makes all your installed Electron apps use i

Gergő Móricz 12 Mar 20, 2023
A Diablo II library for core and simple client functionality, written in Rust for performance, safety and re-usability

A Diablo II library for core and simple client functionality, written in Rust for performance, safety and re-usability

null 4 Nov 30, 2022
The axiom profiler for exploring and visualizing SMT solver quantifier instantiations (made via E-matching).

Axiom Profiler A tool for visualising, analysing and understanding quantifier instantiations made via E-matching in a run of an SMT solver (at present

Viper Project 18 Oct 18, 2022
Code for connecting an RP2040 to a Bosch BNO055 IMU and having the realtime orientation data be sent to the host machine via serial USB

Code for connecting an RP2040 (via Raspberry Pi Pico) to a Bosch BNO055 IMU (via an Adafruit breakout board) and having the realtime orientation data be sent to the host machine via serial USB.

Gerald Nash 3 Nov 4, 2022
Adapter plugin to use Ruff in dprint's CLI and with JavaScript via Wasm

dprint-plugin-ruff Adapter for Ruff for use as a formatting plugin in dprint. Formats .py and .pyi files. Note: For formatting .ipynb files, use the J

null 3 Nov 28, 2023