Export Functions

Guide to export functions from your Rust plugin to be used by other language modules within Plugify.

In the Plugify ecosystem, Rust plugins can export functions to make them accessible to other plugins. This guide explains how to export functions in Rust and provides examples to help you integrate your plugins seamlessly.

Basic Type Mapping

The following table lists how types are exposed to the Rust API:

C++ TypeRust TypePlugify AliasRef Support?
void()void
boolboolbool
chari8char8
char16_tu16char16
int8_ti8int8
int16_ti16int16
int32_ti32int32
int64_ti64int64
uint8_tu8uint8
uint16_tu16uint16
uint32_tu32uint32
uint64_tu64uint64
uintptr_tusizeptr64
uintptr_tusizeptr32
floatf32float
doublef64double
void**const c_voidfunction
plg::stringStrstring
plg::anyAnyany
plg::vector<bool>Arr<bool>bool[]
plg::vector<char>Arr<i8>char8[]
plg::vector<char16_t>Arr<u16>char16[]
plg::vector<int8_t>Arr<i8>int8[]
plg::vector<int16_t>Arr<i16>int16[]
plg::vector<int32_t>Arr<i32>int32[]
plg::vector<int64_t>Arr<i64>int64[]
plg::vector<uint8_t>Arr<u8>uint8[]
plg::vector<uint16_t>Arr<u16>uint16[]
plg::vector<uint32_t>Arr<u32>uint32[]
plg::vector<uint64_t>Arr<u64>uint64[]
plg::vector<uintptr_t>Arr<usize>ptr64[]
plg::vector<uintptr_t>Arr<usize>ptr32[]
plg::vector<float>Arr<f32>float[]
plg::vector<double>Arr<f64>double[]
plg::vector<plg::string>Arr<Str>string[]
plg::vector<plg::any>Arr<Any>any[]
plg::vector<plg::vec2>Arr<Vec2>vec2[]
plg::vector<plg::vec3>Arr<Vec3>vec3[]
plg::vector<plg::vec4>Arr<Vec4>vec4[]
plg::vector<plg::mat4x4>Arr<Mat4x4>mat4x4[]
plg::vec2Vec2vec2
plg::vec3Vec3vec3
plg::vec4Vec4vec4
plg::mat4x4Mat4x4mat4x4

Exporting Functions in Rust

To export a function in a Rust plugin, you need to ensure that the function is visible to other plugins. This is done by using the #[unsafe(no_mangle)] attribute macro provided by the Plugify crate.

Key Points

  • No-mangle: The macro automatically applies #[no_mangle] to prevent name mangling.
  • Extern "C": Functions are exported with C linkage for cross-language compatibility.
  • Parameter and Return Types: Use Plugify's native types for seamless integration.

Basic Example

Here's a simple example of exporting a function in a Rust plugin:

Function Definition

src/lib.rs
use plugify::*;

#[unsafe(no_mangle)]
pub extern "C" fn add_numbers(a: i32, b: i32) -> i32 {
    a + b
}

The #[unsafe(no_mangle)] attribute automatically handles:

  • Exporting the function with C linkage
  • Preventing name mangling
  • Making the function visible to other plugins

Plugin Manifest Example

All exported functions must be described in the plugin's manifest file under the methods section. Here's an example manifest for a plugin that exports the add_numbers function:

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "AddNumbers",
      "funcName": "add_numbers",
      "paramTypes": [
        {
          "type": "int32",
          "name": "a"
        },
        {
          "type": "int32",
          "name": "b"
        }
      ],
      "retType": {
        "type": "int32"
      }
    }
  ]
}

Advanced Example: Exporting Complex Functions

Here's an example of exporting a function with complex parameter and return types:

Function Definition

src/lib.rs
use plugify::*;

#[unsafe(no_mangle)]
pub extern "C" fn process_data(data: &Arr<f64>, prefix: &Str) -> Arr<Str> {
    let collection = data.iter()
        .map(|value| format!("{}{}", prefix, value))
        .collect()
    Arr::from(collection)
}

Plugin Manifest

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "ProcessData",
      "funcName": "process_data",
      "paramTypes": [
        {
          "type": "double[]",
          "name": "data"
        },
        {
          "type": "string",
          "name": "prefix"
        }
      ],
      "retType": {
        "type": "string[]"
      }
    }
  ]
}

Exporting Functions with References

Rust allows you to export functions that take mutable references:

Function Definition

src/lib.rs
use plugify::*;

#[unsafe(no_mangle)]
pub extern "C" fn increment_value(value: &mut i32) {
    *value += 1;
}

Plugin Manifest

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "IncrementValue",
      "funcName": "increment_value",
      "paramTypes": [
        {
          "type": "int32",
          "name": "value",
          "ref": true
        }
      ],
      "retType": {
        "type": "void"
      }
    }
  ]
}

Handling Callbacks

Plugify allows you to export functions that accept callbacks as parameters:

Function Definition

src/lib.rs
use plugify::*;

type CallbackFunction = extern "C" fn(i32, &Str) -> Str;

#[unsafe(no_mangle)]
pub extern "C" fn execute_with_callback(value: i32, input: &Str, callback: CallbackFunction) {
    let result = callback(value, input);
    println!("Callback result: {}", result);
}

Plugin Manifest

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "ExecuteWithCallback",
      "funcName": "execute_with_callback",
      "paramTypes": [
        {
          "type": "int32",
          "name": "value"
        },
        {
          "type": "string",
          "name": "input"
        },
        {
          "type": "function",
          "name": "callback",
          "prototype": {
            "name": "ExampleCallback",
            "funcName": "ExampleCallback",
            "paramTypes": [
              {
                "type": "int32",
                "name": "value"
              },
              {
                "type": "string",
                "name": "input"
              }
            ],
            "retType": {
              "type": "string"
            }
          }
        }
      ],
      "retType": {
        "type": "void"
      }
    }
  ]
}

Working with Enums

Rust enums can be exported to Plugify:

Enum Definition

src/lib.rs
use plugify::*;

#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum LogLevel {
    Debug = 0,
    Info = 1,
    Warning = 2,
    Error = 3,
}
vector_enum_traits!(LogLevel, u8);

#[unsafe(no_mangle)]
pub extern "C" fn log_message(level: LogLevel, message: &Str) {
    match level {
        LogLevel::Debug => println!("[DEBUG] {}", message),
        LogLevel::Info => println!("[INFO] {}", message),
        LogLevel::Warning => println!("[WARNING] {}", message),
        LogLevel::Error => println!("[ERROR] {}", message),
    }
}

Plugin Manifest with Enum

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "language": "rust",
  "methods": [
    {
      "name": "LogMessage",
      "funcName": "log_message",
      "paramTypes": [
        {
          "type": "uint8",
          "name": "level",
          "enum": {
            "name": "LogLevel",
            "values": [
              { "name": "Debug", "value": 0 },
              { "name": "Info", "value": 1 },
              { "name": "Warning", "value": 2 },
              { "name": "Error", "value": 3 }
            ]
          }
        },
        {
          "type": "string",
          "name": "message"
        }
      ],
      "retType": {
        "type": "void"
      }
    }
  ]
}

Best Practices

  1. Use unsafe with extern "C": Always use the #[unsafe(no_mangle)] macro to export functions.
  2. Follow Type Conventions: Use Plugify's native types for parameters and return values.
  3. Document Your Functions: Use Rust doc comments (///) to document exported functions.
  4. Keep Manifest Updated: Ensure the manifest accurately reflects your exported functions.
  5. Use repr for Enums: Always specify #[repr(...)] for enums used in exported functions.
  6. Test Thoroughly: Test your exported functions to ensure they work as expected when called by other plugins.
  7. Handle Errors Gracefully: Consider using Result<T, E> types and convert errors appropriately.

Error Handling

When exporting functions that can fail, consider using a pattern like this:

src/lib.rs
use plugify::*;

#[unsafe(no_mangle)]
pub fn divide_numbers(a: f64, b: f64) -> Var {
    if b == 0.0 {
        Var::from(Any::String("Division by zero"))
    } else {
        Var::from(Any::Float(a / b))
    }
}

Conclusion

Exporting functions in Rust plugins is straightforward when you follow Plugify's conventions and best practices. By using the #[unsafe(no_mangle)] attribute, adhering to type conventions, and maintaining accurate manifest files, you can create robust and interoperable plugins. Rust's type safety and ownership system provide additional guarantees that help prevent common errors in plugin development.