Origin. Assignment from the Yandex Practicum Rust course (FFI module). The task: load image-processing plugins at runtime across a C ABI without letting panics unwind across the boundary. Developed locally, published as a snapshot.
A small CLI that loads a PNG, applies an image-processing plugin (a dynamic library loaded at runtime), and saves the result as PNG. The interesting part is the plugin boundary: pixels and parameters are passed across a C ABI, and each plugin isolates its own panics so nothing unwinds across FFI.
| Crate | Kind | Purpose |
|---|---|---|
plugin_interface |
rlib | Shared process_image signature, the one safe FFI adapter (run_plugin), and key=value parameter helpers. |
image_processor |
bin+lib | Host: CLI (clap), image I/O (image), dynamic loading (libloading), errors (thiserror). |
mirror_plugin |
cdylib | mirror.dll / libmirror.so — horizontal/vertical flip. |
blur_plugin |
cdylib | blur.dll / libblur.so — separable box blur. |
Every plugin exports a single C-compatible function:
#[unsafe(no_mangle)]
pub extern "C" fn process_image(
width: u32,
height: u32,
rgba_data: *mut u8, // width * height * 4 bytes, modified in place
params: *const c_char, // NUL-terminated parameter string
);The buffer is RGBA, 4 bytes per pixel, modified in place. The plugin owns
parsing params and must not panic across the boundary — all plugins forward to
plugin_interface::run_plugin, which reconstructs the slice, decodes the
parameters and wraps the work in catch_unwind.
The host turns a bare --plugin <name> into a file name with the platform's
DLL_PREFIX/DLL_SUFFIX (mirror → mirror.dll on Windows, libmirror.so on
Linux, libmirror.dylib on macOS) and looks for it under --plugin-path.
cargo build --workspaceThe plugin libraries land in target/debug/ (mirror.dll, blur.dll, …),
which is also the default --plugin-path.
# Mirror (horizontal flip)
cargo run -p image_processor -- \
--input input.png \
--output out_mirror.png \
--plugin mirror \
--params params/mirror.txt
# Blur
cargo run -p image_processor -- \
--input input.png \
--output out_blur.png \
--plugin blur \
--params params/blur.txt--plugin-path is optional and defaults to target/debug. Set RUST_LOG=info
(or debug) for progress logging from the host.
Plain key=value, separated by commas or newlines; keys are case-insensitive.
| Plugin | Keys | Defaults | Example |
|---|---|---|---|
mirror |
horizontal, vertical (bool) |
both false |
horizontal=true,vertical=false |
blur |
radius, iterations (u32) |
radius=1,iterations=1 |
radius=4,iterations=3 |
Unknown keys, malformed entries and wrong value types are reported; on a parameter error the plugin leaves the image unchanged.
cargo test --workspaceCovers parameter parsing for both plugins (valid / empty / unknown key / bad value), the mirror logic on a 2×2 buffer (byte-exact), and the blur logic (uniform image unchanged; a single bright pixel spreads to its neighbours).