You can create your own plugins for RDL in the Rust language.
To simplify plugin development, a special crate named netter_plugger has been created (crates.io).
Installing the crate:
cargo add netter_plugger - this command adds the crate for creating plugins to your code dependencies.
Let's start with a simple example:
use netter_plugger::{netter_plugin, generate_dispatch_func};
generate_dispatch_func!();
#[netter_plugin]
fn something(
path: String,
) -> Result<String, String> {
let result = "heey".to_string();
if path == "error" {
Err("err from plugin".to_string())
} else {
Ok(format!("{result}: {path}"))
}
}
#[netter_plugin]
fn add_numbers(a: i32, b: i32) -> Result<String, String> {
Ok((a + b).to_string())
}
#[netter_plugin]
fn check_flag(flag: bool) -> Result<String, String> {
if flag {
Ok("Flag is set".to_string())
} else {
Ok("Flag is not set".to_string())
}
}use netter_plugger::{netter_plugin, generate_dispatch_func};- imports the attribute and macro from the crate for simplification;generate_dispatch_func!();- this macro initializes the entry point for the plugin. It is crucial to place this macro at the very top of your code (right after allusestatements);#[netter_plugin]- an attribute that marks functions for integration into RDL.
Warning
Functions integrated into RDL have important restrictions: Input Data Types The function can only accept the following input types: String, &str, i64, i32, f64, f32, bool. Output Types The function must return the type Result<String, String>.
These restrictions are due to the string-based typing of RDL and interpreter limitations. Over time, the list of supported types will be expanded.
- After the attribute, the function and all its logic are declared as usual.
All your function code is converted to C-compatible types, which involves unsafe extern "C" (you can read more about it here).
Keep this in mind, although problems are usually rare.
To call your plugin functions from RDL, you need to generate a dynamic library from your plugin code:
Note
Your Rust project must be initialized with the --lib flag:
cargo init --lib
Add the following to your Cargo.toml:
[dependencies]
syn = { version = "2.0", features = ["full"] }
quote = "1.0"
lazy_static = "1.5.0"
serde = "1.0.219"
serde_json = "1.0.140"
ctor = "0.4.2"
netter_plugger = "0.1.0"
...
[lib]
crate-type = ["cdylib"]All dependencies listed in the code above must be added to your Cargo.toml. Choose the latest plugin version to avoid compatibility issues and to access all the crate's functionality!
Run the following command in the terminal:
cargo build --releaseThis will generate a dynamic library (.dll or .so).
Before declaring routes (the keyword route), you need to import the previously built dynamic library:
import "path/to/file.dll" as plugin_alias; // "path/to/file.dll" - relative or absolute path to your dynamic library
// plugin_alias - alias for future use
After this, you can use your functions in the code:
route "/" GET {
val a = 1;
val b = 2;
val plugin = plugin_alias::add_numbers(a, b)?;
Response.body(plugin);
Response.send();
} onError(e) {
Response.status(500);
Response.body(e);
Response.send();
};
Important
You may have noticed the ? after calling the plugin function. It is required to catch an error if the function encounters one, so you need to handle this error just like any other errors.
However, if you are confident that no error will occur, you can ignore it using !!, but in case of an error, the code will terminate with a panic (emergency exit).