Rust插件库yaup的使用:高效扩展Rust功能的插件管理与开发工具

Rust插件库yaup的使用:高效扩展Rust功能的插件管理与开发工具

Yaup是一个用于将结构体序列化为查询参数的Rust库。它专门设计用于处理类似Meilisearch使用的查询参数格式。

特性

  • 如果有参数要发送,库会自动写入初始的?
  • 只能序列化遵循"键值"形状的结构,如结构体、HashMapBTreeMap
  • 序列(数组、向量、元组等)用逗号分隔。例如{ 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(&params).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(&params).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),
}

注意事项

  1. 插件必须编译为动态库(.so.dll.dylib
  2. 跨平台使用时需要注意库文件扩展名
  3. 使用unsafe块加载和卸载插件是必要的
  4. 插件和主程序需要使用相同的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.");
}

构建和运行步骤

  1. 首先构建插件接口和实现:
cd my_plugin_interface && cargo build
cd ../my_plugin_impl && cargo build
  1. 然后构建并运行主程序:
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创建一个完整的插件系统,包括接口定义、插件实现和主程序加载使用插件的完整流程。

回到顶部