Rust插件库yaup的使用:高效扩展Rust功能的插件管理与开发工具
Rust插件库yaup的使用:高效扩展Rust功能的插件管理与开发工具
Yaup是一个用于将结构体序列化为查询参数的Rust库。它专门设计用于处理类似Meilisearch使用的查询参数格式。
特性
- 如果有参数要发送,库会自动写入初始的
?
- 只能序列化遵循"键值"形状的结构,如结构体、
HashMap
、BTreeMap
等 - 序列(数组、向量、元组等)用逗号分隔。例如
{ doggo: vec!["kefir", "echo"] }
会序列化为?doggo=kefir,echo
- 空值和
null
值不会被忽略。例如{ doggo: Vec::new(), catto: None }
会序列化为?doggo=&catto=null
- 如果尝试序列化具有多级键值结构的结构(即包含
HashMap
的对象),会返回错误
示例
#[derive(Debug, serde::Serialize)]
enum Filter { New, Registered, Blocked }
#[derive(Debug, serde::Serialize)]
struct Params {
cursor: Option<usize>,
per_page: Option<usize>,
username: String,
filter: Vec<Filter>,
}
let params = Params {
cursor: Some(42),
per_page: None,
username: String::from("tamo"),
filter: vec![Filter::New, Filter::Blocked],
};
assert_eq!(
yaup::to_string(¶ms).unwrap(),
"?cursor=42&per_page=null&username=tamo&filter=New,Blocked"
);
完整示例代码
// 在Cargo.toml中添加依赖
// yaup = "0.3.1"
use serde::Serialize;
// 定义一个枚举表示过滤条件
#[derive(Debug, Serialize)]
enum Status {
Active,
Inactive,
Pending,
}
// 定义一个结构体表示API请求参数
#[derive(Debug, Serialize)]
struct ApiParams {
page: Option<usize>,
limit: Option<usize>,
search: String,
statuses: Vec<Status>,
sort_by: Option<String>,
}
fn main() {
// 创建参数实例
let params = ApiParams {
page: Some(1),
limit: None,
search: String::from("rust"),
statuses: vec![Status::Active, Status::Pending],
sort_by: Some(String::from("name")),
};
// 序列化为查询字符串
let query_string = yaup::to_string(¶ms).unwrap();
println!("生成的查询字符串: {}", query_string);
// 预期输出:
// ?page=1&limit=null&search=rust&statuses=Active,Pending&sort_by=name
}
许可证
- Apache License, Version 2.0
- MIT License
贡献
除非您明确声明,否则任何有意提交包含在此文档中的贡献,如Apache-2.0许可证中所定义,应如上所述双重许可,不附加任何额外条款或条件。
1 回复
yaup:Rust插件管理与开发工具
介绍
yaup是一个用于Rust的插件管理库,它提供了一种高效的方式来扩展Rust应用程序的功能。yaup允许开发者动态加载和执行插件,而无需重新编译主应用程序,非常适合需要模块化架构和可扩展性的项目。
主要特性
- 轻量级插件系统
- 类型安全的插件接口
- 支持动态加载和卸载
- 跨平台兼容性
- 简单的API设计
使用方法
1. 添加依赖
首先,在Cargo.toml
中添加yaup依赖:
[dependencies]
yaup = "0.1" # 请检查最新版本
2. 定义插件接口
创建一个共享库项目定义插件接口:
// 在插件项目中
use yaup::prelude::*;
#[plugin_interface]
pub trait MyPlugin {
fn process(&self, input: String) -> String;
fn version(&self) -> String;
}
3. 实现插件
在另一个crate中实现插件:
use yaup::prelude::*;
struct MyPluginImpl;
#[plugin_export]
impl MyPlugin for MyPluginImpl {
fn process(&self, input: String) -> String {
format!("Processed: {}", input.to_uppercase())
}
fn version(&self) -> String {
"1.0.0".to_string()
}
}
// 必须导出这个函数
#[no_mangle]
pub fn _yaup_create_plugin() -> Box<dyn MyPlugin> {
Box::new(MyPluginImpl)
}
4. 在主程序中使用插件
use yaup::prelude::*;
use std::path::Path;
fn main() {
// 加载插件
let plugin = unsafe { yaup::load::<dyn MyPlugin>(Path::new("target/debug/libmy_plugin.so")) }
.expect("Failed to load plugin");
// 使用插件
println!("Plugin version: {}", plugin.version());
let result = plugin.process("hello world".to_string());
println!("{}", result); // 输出: "Processed: HELLO WORLD"
// 卸载插件
unsafe { yaup::unload(plugin) };
}
高级用法
插件管理器
yaup提供了插件管理器来简化多个插件的管理:
use yaup::manager::PluginManager;
let mut manager = PluginManager::new();
// 加载多个插件
manager.load::<dyn MyPlugin>("path/to/plugin1.so").unwrap();
manager.load::<dyn MyPlugin>("path/to/plugin2.so").unwrap();
// 遍历所有插件
for plugin in manager.plugins::<dyn MyPlugin>() {
println!("Processing with {}", plugin.version());
let result = plugin.process("data".to_string());
println!("Result: {}", result);
}
// 卸载所有插件
manager.unload_all();
错误处理
match yaup::load::<dyn MyPlugin>("path/to/plugin.so") {
Ok(plugin) => {
// 使用插件
let _ = plugin.process("data".to_string());
unsafe { yaup::unload(plugin) };
}
Err(e) => eprintln!("Failed to load plugin: {}", e),
}
注意事项
- 插件必须编译为动态库(
.so
、.dll
或.dylib
) - 跨平台使用时需要注意库文件扩展名
- 使用
unsafe
块加载和卸载插件是必要的 - 插件和主程序需要使用相同的Rust版本编译
yaup为Rust应用程序提供了强大的插件扩展能力,使得应用程序可以保持核心简洁的同时,通过插件提供丰富的功能扩展。
完整示例Demo
下面是一个完整的yaup使用示例,包含主程序、插件接口定义和插件实现:
1. 插件接口定义 (my_plugin_interface)
Cargo.toml:
[package]
name = "my_plugin_interface"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"] # 必须编译为动态库
[dependencies]
yaup = "0.1"
src/lib.rs:
use yaup::prelude::*;
// 定义插件接口
#[plugin_interface]
pub trait MyPlugin {
fn process(&self, input: String) -> String;
fn version(&self) -> String;
}
2. 插件实现 (my_plugin_impl)
Cargo.toml:
[package]
name = "my_plugin_impl"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"] # 必须编译为动态库
[dependencies]
my_plugin_interface = { path = "../my_plugin_interface" }
yaup = "0.1"
src/lib.rs:
use my_plugin_interface::MyPlugin;
use yaup::prelude::*;
// 插件实现结构体
struct MyPluginImpl;
// 实现插件接口
#[plugin_export]
impl MyPlugin for MyPluginImpl {
fn process(&self, input: String) -> String {
format!("[PLUGIN] Processed: {}", input.to_uppercase())
}
fn version(&self) -> String {
"1.0.0".to_string()
}
}
// 必须导出的插件创建函数
#[no_mangle]
pub fn _yaup_create_plugin() -> Box<dyn MyPlugin> {
Box::new(MyPluginImpl)
}
3. 主程序 (main_app)
Cargo.toml:
[package]
name = "main_app"
version = "0.1.0"
edition = "2021"
[dependencies]
my_plugin_interface = { path = "../my_plugin_interface" }
yaup = "0.1"
src/main.rs:
use my_plugin_interface::MyPlugin;
use yaup::prelude::*;
use std::path::Path;
fn main() {
println!("Starting main application...");
// 加载插件
let plugin_path = Path::new("target/debug/libmy_plugin_impl.so");
println!("Loading plugin from: {:?}", plugin_path);
let plugin = unsafe { yaup::load::<dyn MyPlugin>(plugin_path) }
.expect("Failed to load plugin");
// 使用插件功能
println!("Plugin version: {}", plugin.version());
let result = plugin.process("hello from main app".to_string());
println!("Plugin result: {}", result);
// 卸载插件
unsafe { yaup::unload(plugin) };
println!("Plugin unloaded, exiting main application.");
}
构建和运行步骤
- 首先构建插件接口和实现:
cd my_plugin_interface && cargo build
cd ../my_plugin_impl && cargo build
- 然后构建并运行主程序:
cd ../main_app && cargo run
预期输出
Starting main application...
Loading plugin from: "target/debug/libmy_plugin_impl.so"
Plugin version: 1.0.0
Plugin result: [PLUGIN] Processed: HELLO FROM MAIN APP
Plugin unloaded, exiting main application.
这个完整示例展示了如何使用yaup创建一个完整的插件系统,包括接口定义、插件实现和主程序加载使用插件的完整流程。