Rust插件库yare的使用:轻量级、高效插件系统开发框架

Rust插件库yare的使用:轻量级、高效插件系统开发框架

Yare是一个基于过程宏的参数化测试库,允许使用单个测试定义来测试多种不同的输入场景。测试用例可以通过parameterized属性来定义,而不是使用test属性。

特性

  • 参数化:指定不同的输入来测试多个场景
  • 灵活性:参数化测试用例参数可以是表达式
  • 开箱即用:适用于任何Rust版本,无需自定义测试框架
  • 可重用性:定义一次测试用例,在不同测试中重复使用
  • 可读性:使用熟悉的Rust属性语法保持代码可读性
  • 可识别性:每个测试用例都有用户定义的名称
  • 经过实战检验:已在多个crate中使用多年

示例代码

第一个示例

fn add5<T: Into<u32>>(component: T) -> u32 {
    component.into() + 5
}

#[cfg(test)]
mod tests {
    use super::*;
    use yare::parameterized;

    #[parameterized(
      zero_plus_five = { 0, 5 },
      one_plus_five = { 1, 6 },
      two_plus_five = { 2, 7 },
    )]
    fn test_add5(input: u16, expected: u32) {
        assert_eq!(add5(input), expected);
    }
}

带有枚举值的示例

enum Fruit {
    Apple,
    Bramble(BrambleFruit),
    Pear,
}

trait NameOf {
    fn name_of(&self) -> &str;
}

impl NameOf for Fruit {
    fn name_of(&self) -> &str {
        match self {
            Fruit::Apple => "apple",
            Fruit::Bramble(fruit) => fruit.name_of(),
            Fruit::Pear => "pear",
        }
    }
}

enum BrambleFruit {
    Blackberry,
}

impl NameOf for BrambleFruit {
    fn name_of(&self) -> &str {
        match self {
            BrambleFruit::Blackberry => "blackberry",
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use yare::parameterized;

    #[parameterized(
      apple = { Fruit::Apple, "apple" },
      pear = { Fruit::Pear, "pear" },
      blackberry = { Fruit::Bramble(BrambleFruit::Blackberry), "blackberry" },
    )]
    fn a_fruity_test(fruit: Fruit, name: &str) {
        assert_eq!(fruit.name_of(), name)
    }
}

完整示例Demo

use std::sync::atomic::{AtomicU32, Ordering};
use yare::parameterized;

// 模拟掷骰子函数
fn roll_dice(seed: &AtomicU32) -> u8 {
    let mut current = seed.load(Ordering::Relaxed);
    loop {
        let new = current.wrapping_mul(1664525).wrapping_add(1013904223);
        match seed.compare_exchange_weak(current, new, Ordering::Relaxed, Ordering::Relaxed) {
            Ok(_) => return (new % 6 + 1) as u8,  // 返回1-6之间的随机数
            Err(e) => current = e,
        }
    }
}

#[parameterized(
    test_case_1 = { roll_dice(&AtomicU32::new(0)), 1 },  // 固定种子测试
    test_case_2 = { roll_dice(&AtomicU32::new(42)), 4 }, // 不同种子测试
)]
fn test_dice_roll(seed: u32, expected_min: u8) {
    let seed = AtomicU32::new(seed);
    let result = roll_dice(&seed);
    assert!(result >= expected_min && result <= 6);
}

// 异步测试示例
#[parameterized(
    fast_case = { 50, 50 },
    slow_case = { 200, 200 },
)]
#[test_macro(tokio::test)]
async fn test_async_sleep(wait_ms: u64, expected_elapsed: u128) {
    let start = std::time::Instant::now();
    tokio::time::sleep(tokio::time::Duration::from_millis(wait_ms)).await;
    let elapsed = start.elapsed().as_millis();
    assert!(elapsed >= expected_elapsed);
}

// 带有返回值的测试
#[parameterized(
    success_case = { Ok(42) },
    error_case = { Err("test error") },
)]
fn test_with_result(result: Result<i32, &str>) -> Result<(), &str> {
    let value = result?;
    assert_eq!(value, 42);
    Ok(())
}

如何使用

  1. 添加依赖到Cargo.toml:
yare = "3.0.0"
  1. 在测试模块中使用:
use yare::parameterized;

#[parameterized(
    case1 = { param1, param2 },
    case2 = { param3, param4 },
)]
fn test_function(arg1: Type1, arg2: Type2) {
    // 测试逻辑
}

许可证

Yare采用双许可证:

  • MIT许可证
  • Apache-2.0许可证

您可以选择其中任一许可证使用。


1 回复

Rust插件库yare的使用:轻量级、高效插件系统开发框架

介绍

yare是一个轻量级的Rust插件系统开发框架,它提供了一种简单高效的方式来构建可扩展的应用程序。yare特别适合需要动态加载和卸载功能的场景,如游戏模组、编辑器插件或任何需要运行时扩展的系统。

主要特点:

  • 轻量级设计,最小化开销
  • 类型安全的插件接口
  • 支持热重载插件
  • 简单的API设计
  • 跨平台支持

完整示例demo

下面是一个完整的yare插件系统示例,包含主程序和插件实现:

主程序 (main.rs)

use yare::PluginManager;
use std::path::Path;

// 定义插件接口
pub trait Plugin {
    fn name(&self) -> &str;
    fn on_load(&self);
    fn on_unload(&self);
    fn execute(&self, input: &str) -> String;
}

fn main() {
    // 创建插件管理器
    let mut manager = PluginManager::<dyn Plugin>::new();
    
    // 加载插件
    #[cfg(target_os = "linux")]
    let plugin_path = Path::new("target/debug/libdemo_plugin.so");
    #[cfg(target_os = "windows")]
    let plugin_path = Path::new("target/debug/demo_plugin.dll");
    #[cfg(target_os = "macos")]
    let plugin_path = Path::new("target/debug/libdemo_plugin.dylib");
    
    match manager.load(plugin_path) {
        Ok(handle) => {
            // 获取并使用插件
            if let Some(plugin) = manager.get_instance(handle) {
                println!("[主程序] 已加载插件: {}", plugin.name());
                plugin.on_load();
                
                let result = plugin.execute("测试插件功能");
                println!("[主程序] 插件执行结果: {}", result);
                
                plugin.on_unload();
            }
            
            // 卸载插件
            manager.unload(handle);
        }
        Err(e) => eprintln!("[主程序] 加载插件失败: {}", e),
    }
}

插件实现 (lib.rs)

use yare::plugin_export;

// 使用宏导出插件
#[plugin_export]
pub struct DemoPlugin;

// 实现插件接口
impl super::Plugin for DemoPlugin {
    fn name(&self) -> &str {
        "DemoPlugin"
    }
    
    fn on_load(&self) {
        println!("[插件] 已加载");
    }
    
    fn on_unload(&self) {
        println!("[插件] 已卸载");
    }
    
    fn execute(&self, input: &str) -> String {
        // 简单的处理逻辑 - 反转字符串并转为大写
        let processed = input.chars().rev().collect::<String>().to_uppercase();
        format!("处理结果: {}", processed)
    }
}

Cargo.toml配置

主程序Cargo.toml:

[package]
name = "yare_demo_host"
version = "0.1.0"
edition = "2021"

[dependencies]
yare = "0.3"

插件Cargo.toml:

[package]
name = "yare_demo_plugin"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]  # 重要:必须设置为cdylib

[dependencies]
yare = "0.3"

构建和运行步骤

  1. 首先构建插件:
cd yare_demo_plugin
cargo build
  1. 然后运行主程序:
cd yare_demo_host
cargo run
  1. 预期输出:
[主程序] 已加载插件: DemoPlugin
[插件] 已加载
[主程序] 插件执行结果: 处理结果: 能功件插试测
[插件] 已卸载

高级功能示例

带配置的插件实现

// 在插件接口中添加
pub trait ConfigurablePlugin: Plugin {
    fn configure(&mut self, config: &str);
}

// 在插件实现中添加
#[plugin_export]
pub struct ConfigurableDemoPlugin {
    prefix: String,
}

impl ConfigurableDemoPlugin {
    pub fn new() -> Self {
        Self {
            prefix: "默认: ".to_string(),
        }
    }
}

impl super::Plugin for ConfigurableDemoPlugin {
    // ... 其他实现 ...
    fn execute(&self, input: &str) -> String {
        format!("{}{}", self.prefix, input.to_uppercase())
    }
}

impl super::ConfigurablePlugin for ConfigurableDemoPlugin {
    fn configure(&mut self, config: &str) {
        self.prefix = format!("配置[{}]: ", config);
    }
}

注意事项

  1. 插件和主程序必须使用相同的Rust版本编译
  2. 类型必须完全一致,包括所有依赖项的版本
  3. 在Windows上需要注意DLL的加载/卸载策略
  4. 建议为插件接口设计稳定的ABI

这个完整示例展示了如何使用yare创建一个简单的插件系统,包含插件加载、执行和卸载的全过程。通过这个框架,您可以轻松扩展应用程序的功能,实现动态模块加载。

回到顶部