# miniffi
> [!WARNING]
> This project is on its first release. I'm still trying out the idea. It has
> not been used in production.
This is a simple but opinionated FFI system for Rust. It allows you to call Rust code from other
languages, and vice versa. Some design principles:
- Intended for writing platform-agnostic Rust with platform-specific details in the host language
- FFI support is split into data (structs passed by copy) and code (traits passed by reference)
- Rather than have a separate schema language, the public API in your `lib.rs` is your schema
- The generated binding code is compact, dependency-free, and straightforward to integrate with
## Supported Features
- Primitive types (`bool`, `i32`, `f64`, `String`, etc.)
- Tuples
- Structs
- Enums
- Top-level constants
- Top-level functions
- Traits (must be either `Box<dyn T>` or `Rc<dyn T>`)
- `Vec<T>`
- `Option<T>`
- `Box<T>`
## Available Targets
- JavaScript/TypeScript (WASM)
- Swift
- C++
## Example
After setting everything up, using miniffi looks something like this. The
following example Rust code (in `lib.rs`) demonstrates passing code and
data back and forth between Rust and the host language:
```rs
pub enum Level {
Warning,
Error,
}
pub trait Logger {
fn log(&self, level: Level, text: &str);
}
pub trait Demo {
fn run(&self, logger: Box<dyn Logger>);
}
pub fn get_demo() -> Box<dyn Demo> {
struct DemoImpl;
impl Demo for DemoImpl {
fn run(&self, logger: Box<dyn Logger>) {
logger.log(Level::Warning, "example");
}
}
Box::new(DemoImpl)
}
// This includes the binding code generated by miniffi
include!(concat!(env!("OUT_DIR"), "/miniffi.rs"));
```
Unlike other FFI systems, there are no annotations required to expose something
from `lib.rs` to the host language other than using `pub` to mark things public.
The miniffi build script parses `lib.rs`, extracts the supported parts of the
public API, and generates FFI binding code both for Rust (the `miniffi.rs` file
referenced above) and for the host language. Calling that example Rust code
might look like this:
In JavaScript/TypeScript:
```js
import * as rust from "./miniffi.js"
import fs from "fs"
await rust.instantiate(fs.readFileSync(
"./target/wasm32-unknown-unknown/debug/example.wasm"))
rust.get_demo().run({
log(level, text) {
if (level === rust.Level.Error)
console.error("ERROR:", text)
else if (level === rust.Level.Warning)
console.warn("WARNING:", text)
}
})
```
In Swift:
```swift
class LoggerImpl : Logger {
func log(_ level: Level, _ text: String) {
if level == .Error {
print("ERROR:", text)
} else if level == .Warning {
print("WARNING:", text)
}
}
}
get_demo().run(LoggerImpl())
```
In C++:
```c++
#include <iostream>
#include "ffi.h"
struct LoggerImpl : rust::Logger {
void log(rust::Level level, std::string text) {
if (level == rust::Level::Error)
std::cout << "ERROR: " << text << std::endl;
else if (level == rust::Level::Warning)
std::cout << "WARNING: " << text << std::endl;
}
};
int main() {
rust::get_demo()->run(std::make_unique<LoggerImpl>());
return 0;
}
```
More information about how to use miniffi can be found in the [documentation](https://docs.rs/miniffi/latest/miniffi/).
## Similar Projects
These projects are much further along, and you may want to use them instead:
- [UniFFI](https://github.com/mozilla/uniffi-rs)
- [Diplomat](https://github.com/rust-diplomat/diplomat)
- [Interoptopus](https://github.com/ralfbiedert/interoptopus)