Rust插件库argyle的使用:高效灵活的插件系统扩展库

Rust插件库argyle的使用:高效灵活的插件系统扩展库

Argyle是一个简单的流式CLI参数解析器/迭代器,提供了介于标准库的std::env::args_os和完整服务库如clap之间的中间解决方案。

功能特点

  • 执行基本规范化处理
  • 以非panic方式处理字符串转换
  • 识别简写值赋值如-kval-k=val--key=val
  • 处理命令结束(--)参数
  • 帮助识别应用程序预期的特殊键/值

安装

Cargo.tomldependencies中添加:

[dependencies]
argyle = "0.13.*"

示例代码

以下是内容中提供的示例:

use argyle::{Argument, KeyWord};
use std::path::PathBuf;

#[derive(Debug, Clone, Default)]
/// # Configuration.
struct Settings {
    threads: usize,
    verbose: bool,
    paths: Vec<PathBuf>,
}

let args = argyle::args()
    .with_keywords([
        KeyWord::key("-h").unwrap(),            // Boolean flag (short).
        KeyWord::key("--help").unwrap(),        // Boolean flag (long).
        KeyWord::key_with_value("-j").unwrap(), // Expects a value.
        KeyWord::key_with_value("--threads").unwrap(),
    ]);

// Loop and handle!
let mut settings = Settings::default();
for arg in args {
    match arg {
        // Help flag match.
        Argument::Key("-h" | "--help") => {
            println!("Help Screen Goes Here.");
            return;
        },

        // Thread option match.
        Argument::KeyWithValue("-j" | "--threads", value) => {
            settings.threads = value.parse()
                .expect("Maximum threads must be a number!");
        },

        // Something else.
        Argument::Other(v) => {
            settings.paths.push(PathBuf::from(v));
        },

        // Also something else, but not String-able. PathBuf doesn't care
        // about UTF-8, though, so it might be fine!
        Argument::InvalidUtf8(v) => {
            settings.paths.push(PathBuf::from(v));
        },

        // Nothing else is relevant here.
        _ => {},
    }
}

// Now that you're set up, do stuff…

完整示例demo

基于上述示例,这里是一个更完整的命令行参数处理demo:

use argyle::{Argument, KeyWord};
use std::path::PathBuf;

#[derive(Debug, Clone, Default)]
struct AppConfig {
    threads: usize,
    verbose: bool,
    input_files: Vec<PathBuf>,
    output_dir: Option<PathBuf>,
}

fn main() {
    // 定义支持的参数
    let args = argyle::args()
        .with_keywords([
            KeyWord::key("-h").unwrap(),            // 帮助标志(短)
            KeyWord::key("--help").unwrap(),        // 帮助标志(长)
            KeyWord::key("-v").unwrap(),           // 详细模式标志
            KeyWord::key("--verbose").unwrap(),     // 详细模式标志
            KeyWord::key_with_value("-j").unwrap(), // 线程数
            KeyWord::key_with_value("--threads").unwrap(),
            KeyWord::key_with_value("-o").unwrap(), // 输出目录
            KeyWord::key_with_value("--output").unwrap(),
        ]);

    let mut config = AppConfig {
        threads: 4,  // 默认4个线程
        ..Default::default()
    };

    // 处理参数
    for arg in args {
        match arg {
            // 帮助标志
            Argument::Key("-h" | "--help") => {
                print_help();
                return;
            },

            // 详细模式标志
            Argument::Key("-v" | "--verbose") => {
                config.verbose = true;
            },

            // 线程数设置
            Argument::KeyWithValue("-j" | "--threads", value) => {
                config.threads = value.parse()
                    .expect("线程数必须是正整数!");
            },

            // 输出目录设置
            Argument::KeyWithValue("-o" | "--output", value) => {
                config.output_dir = Some(PathBuf::from(value));
            },

            // 输入文件
            Argument::Other(v) => {
                config.input_files.push(PathBuf::from(v));
            },

            // 非UTF-8路径
            Argument::InvalidUtf8(v) => {
                config.input_files.push(PathBuf::from(v));
            },

            // 忽略其他参数
            _ => {},
        }
    }

    // 检查是否有输入文件
    if config.input_files.is_empty() {
        eprintln!("错误: 未指定输入文件");
        print_help();
        std::process::exit(1);
    }

    // 使用配置运行程序
    run_app(config);
}

fn print_help() {
    println!("用法: myapp [选项] 输入文件...");
    println!();
    println!("选项:");
    println!("  -h, --help        显示帮助信息");
    println!("  -v, --verbose     启用详细输出");
    println!("  -j, --threads N   设置线程数 (默认: 4)");
    println!("  -o, --output DIR  设置输出目录");
}

fn run_app(config: AppConfig) {
    println!("运行配置: {:?}", config);
    // 这里实现实际的应用程序逻辑
    // ...
}

特性

可以通过启用非默认的try_paths特性来暴露额外的Argument::Path变体,用于未关联且未识别的值,其中std::fs::exists() == Ok(true)

许可证

本项目采用WTFPL许可证。


1 回复

Rust插件库argyle的使用:高效灵活的插件系统扩展库

介绍

argyle是一个Rust语言的插件系统扩展库,它提供了高效灵活的插件管理功能。这个库特别适合需要动态加载和卸载功能模块的应用场景,如编辑器、游戏引擎或任何需要扩展性的应用程序。

argyle的主要特点包括:

  • 类型安全的插件接口
  • 跨平台支持
  • 低开销设计
  • 灵活的插件生命周期管理
  • 支持热重载

使用方法

基本使用

首先在Cargo.toml中添加依赖:

[dependencies]
argyle = "0.3"

定义插件接口

use argyle::ArgylePlugin;

// 定义插件trait
pub trait GreeterPlugin: ArgylePlugin {
    fn greet(&self, name: &str) -> String;
}

实现插件

use argyle::{ArgylePlugin, PluginMetadata};
use std::path::Path;

struct HelloPlugin;

impl ArgylePlugin for HelloPlugin {
    fn metadata(&self) -> PluginMetadata {
        PluginMetadata {
            name: "Hello Plugin".into(),
            version: "1.0".into(),
        }
    }
}

impl GreeterPlugin for HelloPlugin {
    fn greet(&self, name: &str) -> String {
        format!("Hello, {}!", name)
    }
}

// 导出插件工厂函数
#[no_mangle]
pub extern "C" fn _argyle_create_plugin() -> Box<dyn GreeterPlugin> {
    Box::new(HelloPlugin)
}

加载和使用插件

use argyle::PluginLoader;
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建插件加载器
    let mut loader = PluginLoader::<dyn GreeterPlugin>::new();
    
    // 加载插件
    let plugin = unsafe { loader.load(Path::new("target/debug/libhello_plugin.so"))? };
    
    // 使用插件
    println!("{}", plugin.greet("Rustacean"));
    
    // 卸载插件
    loader.unload(plugin);
    
    Ok(())
}

高级功能:插件热重载

use argyle::{PluginLoader, HotReloadMode};
use std::path::Path;
use std::time::Duration;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut loader = PluginLoader::<dyn GreeterPlugin>::new()
        .with_hot_reload(HotReloadMode::Interval(Duration::from_secs(1)));
    
    let plugin = unsafe { loader.load(Path::new("target/debug/libhello_plugin.so"))? };
    
    loop {
        println!("{}", plugin.greet("World"));
        std::thread::sleep(Duration::from_secs(2));
    }
}

完整示例demo

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

1. 创建主程序项目

cargo new argyle_demo
cd argyle_demo

编辑Cargo.toml:

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

[dependencies]
argyle = "0.3"

2. 创建插件项目

cargo new --lib hello_plugin
cd hello_plugin

编辑Cargo.toml:

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

[lib]
crate-type = ["cdylib"]

[dependencies]
argyle = "0.3"

3. 实现插件

编辑src/lib.rs:

use argyle::{ArgylePlugin, PluginMetadata};

// 定义插件接口
pub trait GreeterPlugin: ArgylePlugin {
    fn greet(&self, name: &str) -> String;
}

// 插件实现
struct HelloPlugin;

impl ArgylePlugin for HelloPlugin {
    fn metadata(&self) -> PluginMetadata {
        PluginMetadata {
            name: "Hello Plugin".into(),
            version: "1.0".into(),
        }
    }
}

impl GreeterPlugin for HelloPlugin {
    fn greet(&self, name: &str) -> String {
        format!("Hello from plugin, {}!", name)
    }
}

// 导出插件工厂函数
#[no_mangle]
pub extern "C" fn _argyle_create_plugin() -> Box<dyn GreeterPlugin> {
    Box::new(HelloPlugin)
}

4. 实现主程序

编辑主项目的src/main.rs:

use argyle::PluginLoader;
use std::path::Path;

// 需要与插件中相同的trait定义
pub trait GreeterPlugin: argyle::ArgylePlugin {
    fn greet(&self, name: &str) -> String;
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 构建插件路径
    let plugin_path = Path::new("../hello_plugin/target/debug/")
        .join(format!("{}hello_plugin.{}", 
            if cfg!(windows) { "" } else { "lib" },
            if cfg!(windows) { "dll" } else if cfg!(target_os = "macos") { "dylib" } else { "so" }
        ));
    
    println!("Loading plugin from: {:?}", plugin_path);
    
    // 创建并加载插件
    let mut loader = PluginLoader::<dyn GreeterPlugin>::new();
    let plugin = unsafe { loader.load(&plugin_path)? };
    
    // 使用插件功能
    println!("{}", plugin.greet("Rust Developer"));
    
    // 卸载插件
    loader.unload(plugin);
    
    Ok(())
}

5. 构建和运行

首先构建插件:

cd hello_plugin
cargo build

然后运行主程序:

cd ../argyle_demo
cargo run

注意事项

  1. 插件需要编译为动态库(.so.dll.dylib
  2. 跨平台使用时要注意ABI兼容性
  3. 热重载功能在开发环境中特别有用,但生产环境可能需要禁用
  4. 确保插件和主程序使用相同版本的Rust编译器

argyle为Rust应用程序提供了强大的插件系统支持,通过合理使用可以极大地提高应用程序的扩展性和灵活性。

回到顶部