---
# System prepended metadata

title: Native Client API

---

# Native Client API

- Instantiate on the application side, using library functions
- Get opaque pointers and use them to call mutating library functions on those opaque structures
- Only pass pointers and serializable configuration data across the boundary

## Path #1: Wrapping the whole client

Things to consider:

- We should allow app-defined resolvers and plugins. I think it'd probably go like this:

1. We define a general-use header file that defines the shape of a PluginWrapper in general:

```hpp!
#ifndef PLUGIN_WRAPPER_HPP
#define PLUGIN_WRAPPER_HPP

#include <cstdint>

class PluginWrapper {
public:
    PluginWrapper();
    virtual ~PluginWrapper();
    virtual int invoke(const uint8_t* data, size_t size) = 0;
};

#endif
```

2. Import the header definitions into the Rust FFI lib:

```rust!
use std::os::raw::{c_char, c_uint};
use libc::size_t;
use crate::plugin_wrapper::PluginWrapper;

#[no_mangle]
pub extern "C" fn invoke_plugin(s: *mut PluginWrapper, data: *const u8, size: size_t) -> c_uint {
    if s.is_null() {
        return 1;
    }
    let plugin_wrapper = unsafe { &mut *s };
    let result = plugin_wrapper.invoke(unsafe { std::slice::from_raw_parts(data, size) });
    result as c_uint
}
```

3. Define a plugin class on the app side, and import the header file as well, and override the PluginWrapper class:

```cpp!
#include "plugin_wrapper.hpp"
#include "ffi_native_lib.h"  // Header file generated by Rust FFI

class MyPlugin : public PluginWrapper {
public:
    virtual int invoke(const uint8_t* data, size_t size) override {
        ...
    }
};
```

4. Pass down the derived class instance to the rust library and invoke it when appropriate in Rust

Some implementation pseudo examples:

- Config Builder Example (pseudo):

```cpp
const char* builder_ptr = ffi_create_client_builder()
ffi_add_resolver(&builder_ptr, resolver1)
ffi_add_resolver(&builder_ptr, resolver2)
ffi_add_resolver(&builder_ptr, resolver3)
    
const char* native_client = build_client(&builder_ptr);
```

- Build an app side struct to wrap native_client function invocations:

```cpp!
NativeClient::NativeClient() {
    client_ = ffi_create_client();  // Call the FFI function to create the RustClient struct
}

NativeClient::~NativeClient() {
    ffi_destroy_client(client_);  // Call the FFI function to destroy the RustClient struct
}

void NativeClient::invoke(const uint8_t* data, size_t size) {
    ffi_invoke(client_, data, size);  // Call the FFI function to invoke the Rust function
}
```

And the usage would roughly look like:

```cpp!
int main() {
    NativeClient client;
    uint8_t data[] = {...msgpack data blabla };
    size_t size = sizeof(data) / sizeof(data[0]);
    client.invoke(data, size);
    return 0;
}
```

## Path 2: Only wrapping Wasm runtime layer + utils

## Useful resources on known obstacles:

- https://adventures.michaelfbryan.com/posts/ffi-safe-polymorphism-in-rust/



# Simple Example Project

1. native DLL w/ stateful store
2. foreign binding interface (swift <> dll)
3. swift app w/ object instance

```swift=
// our app w/ stateful custom objects
class Animal {
    virtual speak();
}

class Cow {
    speak() {
        console.log("moo")
    }
}

// native->swift function bindings
function animal_speak(instancePtr: uint32) {
    const instance = reinterpret_cast<Animal>(instancePtr)
    instance.speak();
}

class Farm {
    private _nativeLib;
    private _farmPtr;
    
    constructor() {
        this._farmPtr = this._nativeLib.create_farm();
    }

    addAnimal(instance: Animal) {
        const animalPtr = ptr_cast(instance);
        const animalFuncs: IAnimalCApi = {
            animal_speak
        };

        this._nativeLib.add_animal(
            this._farmPtr,
            animalPtr,
            animalFuncs
        );
    }
}

function main_demo() {
    const farm = new Farm();
    
    farm.addAnimal(
        new Cow()
    );
    
    farm.speak("cow");
    farm.speak("pig");
    
    // Ex: swift -> native obj
    const cow = farm.getAnimal("cow");
    cow.speak();
}
```

```cpp
void add_animal(...) {
    // PATTERN = passing pointers that reinterpret cast INTO stateful objects
}

void create_farm(): u32 {
    const farm = new Farm();
    
    // NEED: add some default animals
    farm.add(new Pig());

    return &farm;
}
```
