Stability
Learn how lla's plugin system maintains ABI stability through Protocol Buffers and FFI layers. Understand how plugins can remain compatible across different lla versions and Rust compiler versions through careful API design and message-based communication patterns.
ABI Stability
lla’s plugin system keeps ABI stable across lla and Rust versions. Plugins remain compatible.
Core Architecture
Message-Based Foundation
Protocol Buffers is the communication layer between lla and plugins:
message PluginMessage {
oneof message {
bool get_name = 1;
bool get_version = 2;
bool get_description = 3;
// ... other fields
}
}
Benefits:
- No Rust ABI coupling
- Language-neutral
- Version-resilient
Cross-Language Communication
The FFI layer uses C-compatible types:
#[repr(C)]
pub struct RawBuffer {
pub ptr: *mut u8,
pub len: usize,
pub capacity: usize,
}
#[repr(C)]
pub struct PluginApi {
pub version: u32,
pub handle_request: extern "C" fn(*mut std::ffi::c_void, *const u8, usize) -> RawBuffer,
pub free_response: extern "C" fn(*mut RawBuffer),
}
Versioning
Every plugin declares its API version:
pub const CURRENT_PLUGIN_API_VERSION: u32 = 1;
The loader checks compatibility.
Data Interchange
Rich Metadata Exchange
File metadata is exchanged via Protobuf:
message EntryMetadata {
uint64 size = 1;
uint64 modified = 2;
uint64 accessed = 3;
uint64 created = 4;
bool is_dir = 5;
bool is_file = 6;
bool is_symlink = 7;
uint32 permissions = 8;
uint32 uid = 9;
uint32 gid = 10;
}
Extensible Design
Plugins can add custom fields:
message DecoratedEntry {
string path = 1;
EntryMetadata metadata = 2;
map<string, string> custom_fields = 3;
}
Implementation Intelligence
Loading
libloading
handles dynamic loading with isolation and message passing.
Type Safety
- Strict Protobuf schemas
- FFI-compatible types
- Clear conversions
Creating Plugins
Start with the macros and implement the handler:
-
Start with our FFI macros:
declare_plugin!(MyPlugin);
-
Implement core functionality:
impl Plugin for MyPlugin { fn handle_raw_request(&mut self, request: &[u8]) -> Vec<u8> { // Transform requests into responses } }
-
Handle data transformations:
impl From<EntryMetadata> for proto::EntryMetadata { fn from(meta: EntryMetadata) -> Self { // Convert your data structures } }
Use the plugin utils to reduce boilerplate. See other plugins for examples.