Crates.io | lla_plugin_interface |
lib.rs | lla_plugin_interface |
version | 0.3.10 |
source | src |
created_at | 2024-08-13 04:22:19.286635 |
updated_at | 2025-01-06 19:01:45.228081 |
description | Interface for lla plugins |
homepage | |
repository | https://github.com/chaqchase/lla |
max_upload_size | |
id | 1335455 |
size | 20,539 |
lla
- plugin interfaceThis crate provides a plugin interface for the lla
command line tool.
The plugin system in lla
is designed to be robust and version-independent, using a message-passing architecture that ensures ABI compatibility across different Rust versions. Here's how it works:
Protocol Buffer Interface
plugin.proto
, providing a language-agnostic contractFFI Boundary
libloading
The plugin system solves the ABI compatibility problem through several mechanisms:
Message-Based Communication
Version Control
Stable Interface
To create a plugin:
.so
, .dll
, or .dylib
)The main application will handle loading, version verification, and communication with your plugin automatically.
Here's a simple example of a file type categorizer plugin that demonstrates the key concepts:
use lla_plugin_interface::{DecoratedEntry, Plugin};
use prost::Message as ProstMessage;
/// A simple plugin that categorizes files based on their extensions
pub struct SimpleCategorizerPlugin {
categories: Vec<(String, Vec<String>)>, // (category_name, extensions)
}
impl SimpleCategorizerPlugin {
pub fn new() -> Self {
Self {
categories: vec![
("Document".to_string(), vec!["txt", "pdf", "doc"].into_iter().map(String::from).collect()),
("Image".to_string(), vec!["jpg", "png", "gif"].into_iter().map(String::from).collect()),
("Code".to_string(), vec!["rs", "py", "js"].into_iter().map(String::from).collect()),
]
}
}
fn get_category(&self, entry: &DecoratedEntry) -> Option<String> {
let extension = entry.path.extension()?.to_str()?.to_lowercase();
self.categories.iter()
.find(|(_, exts)| exts.contains(&extension))
.map(|(category, _)| category.clone())
}
}
impl Plugin for SimpleCategorizerPlugin {
fn handle_raw_request(&mut self, request: &[u8]) -> Vec<u8> {
use lla_plugin_interface::proto::{self, plugin_message};
// Decode the incoming protobuf message
let proto_msg = match proto::PluginMessage::decode(request) {
Ok(msg) => msg,
Err(e) => return self.encode_error(&format!("Failed to decode request: {}", e)),
};
// Handle different message types
let response_msg = match proto_msg.message {
// Return plugin metadata
Some(plugin_message::Message::GetName(_)) => {
plugin_message::Message::NameResponse("simple-categorizer".to_string())
}
Some(plugin_message::Message::GetVersion(_)) => {
plugin_message::Message::VersionResponse("0.1.0".to_string())
}
Some(plugin_message::Message::GetDescription(_)) => {
plugin_message::Message::DescriptionResponse(
"A simple file categorizer plugin".to_string(),
)
}
// Handle file decoration request
Some(plugin_message::Message::Decorate(entry)) => {
let mut decorated_entry = match DecoratedEntry::try_from(entry.clone()) {
Ok(e) => e,
Err(e) => return self.encode_error(&format!("Failed to convert entry: {}", e)),
};
// Add category to the entry's custom fields
if let Some(category) = self.get_category(&decorated_entry) {
decorated_entry.custom_fields.insert("category".to_string(), category);
}
plugin_message::Message::DecoratedResponse(decorated_entry.into())
}
_ => plugin_message::Message::ErrorResponse("Invalid request type".to_string()),
};
// Encode and return the response
let response = proto::PluginMessage {
message: Some(response_msg),
};
let mut buf = bytes::BytesMut::with_capacity(response.encoded_len());
response.encode(&mut buf).unwrap();
buf.to_vec()
}
}
// Register the plugin with the main application
lla_plugin_interface::declare_plugin!(SimpleCategorizerPlugin);
This example demonstrates:
Plugin
traitThe plugin can be compiled as a dynamic library and loaded by the main application at runtime, with full ABI compatibility regardless of the Rust version used to compile either component.