Rust插件库upon的使用,upon提供灵活的插件机制与扩展功能

Rust插件库upon的使用,upon提供灵活的插件机制与扩展功能

upon是一个简单但功能强大的模板引擎,具有最小依赖和可配置分隔符的特点。

概述

语法特性

  • 表达式: {{ user.name }}
  • 条件语句: {% if user.enabled %} ... {% endif %}
  • 循环: {% for user in users %} ... {% endfor %}
  • 嵌套模板: {% include "nested" %}
  • 可配置分隔符: <? user.name ?>, (( if user.enabled ))
  • 自定义函数: {{ user.name | replace: "\t", " " }}

引擎特性

  • 清晰且文档完善的API
  • 自定义值格式化器
  • 可渲染到String或任何实现std::io::Write的类型
  • 支持任何可serde序列化的值
  • 提供方便的宏用于快速渲染
  • 使用{:#}显示时会有漂亮的错误信息

快速开始

首先添加依赖:

cargo add upon

然后创建引擎实例:

let engine = upon::Engine::new();

添加模板:

engine.add_template("hello", "Hello {{ user.name }}!")?;

渲染模板:

let result = engine
    .template("hello")
    .render(upon::value!{ user: { name: "John Smith" }})
    .to_string()?;
assert_eq!(result, "Hello John Smith!");

完整示例

嵌套模板示例

let mut engine = upon::Engine::new();
engine.add_template("hello", "Hello {{ user.name }}!")?;
engine.add_template("goodbye", "Goodbye {{ user.name }}!")?;
engine.add_template("nested", "{% include \"hello\" %}\n{% include \"goodbye\" %}")?;

let result = engine.template("nested")
    .render(upon::value!{ user: { name: "John Smith" }})
    .to_string()?;
assert_eq!(result, "Hello John Smith!\nGoodbye John Smith!");

渲染到Writer

use std::io;

let mut engine = upon::Engine::new();
engine.add_template("hello", "Hello {{ user.name }}!")?;

let mut stdout = io::BufWriter::new(io::stdout());
engine
    .template("hello")
    .render(upon::value!{ user: { name: "John Smith" }})
    .to_writer(&mut stdout)?;
// 输出: Hello John Smith!

自定义模板存储和函数

let mut store = std::collections::HashMap::<&str, upon::Template>::new();
store.insert("hello", engine.compile("Hello {{ user.name }}!")?);
store.insert("goodbye", engine.compile("Goodbye {{ user.name }}!")?);
store.insert("nested", engine.compile("{% include \"hello\" %}\n{% include \"goodbye\" %}")?);

let result = store.get("nested")
    .unwrap()
    .render(&engine, upon::value!{ user: { name: "John Smith" }})
    .with_template_fn(|name| {
        store
            .get(name)
            .ok_or_else(|| String::from("template not found"))
    })
    .to_string()?;
assert_eq!(result, "Hello John Smith!\nGoodbye John Smith!");

特性

upon支持以下特性:

  • functions (默认启用) - 启用模板中的函数支持
  • serde (默认启用) - 启用所有serde支持
  • syntax (默认禁用) - 启用自定义分隔符配置
  • unicode (默认启用) - 启用Unicode支持

性能

upon在基准测试中表现出色,相比其他流行的Rust模板引擎如Handlebars、Liquid、Tera等,在编译和渲染性能上都有优势。

完整示例demo

下面是一个结合条件语句和循环的完整示例:

use upon::Engine;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建引擎实例
    let mut engine = Engine::new();
    
    // 添加带有条件和循环的模板
    engine.add_template("user_list", r#"
        {% if users.is_empty %}
            No users found.
        {% else %}
            User List:
            {% for user in users %}
            - {{ user.name }} ({{ user.age }} years old)
                {% if user.is_admin %}
                    (Admin)
                {% endif %}
            {% endfor %}
        {% endif %}
    "#)?;
    
    // 准备数据
    let data = upon::value! {
        users: [
            { name: "Alice", age: 25, is_admin: true },
            { name: "Bob", age: 30, is_admin: false },
            { name: "Charlie", age: 35, is_admin: true }
        ]
    };
    
    // 渲染模板
    let result = engine
        .template("user_list")
        .render(data)
        .to_string()?;
    
    println!("{}", result);
    Ok(())
}

运行上述代码将输出:

User List:
- Alice (25 years old)
    (Admin)
- Bob (30 years old)
- Charlie (35 years old)
    (Admin)

这个示例展示了upon模板引擎的主要功能:

  • 使用{% if %}条件语句
  • 使用{% for %}循环
  • 嵌套条件和循环
  • 访问嵌套对象属性
  • 渲染到字符串

1 回复

Rust插件库upon的使用指南

upon是一个提供灵活插件机制与扩展功能的Rust库,它允许开发者轻松地为应用程序添加插件支持,实现模块化扩展。

主要特性

  • 轻量级插件系统
  • 灵活的扩展点机制
  • 支持动态加载和卸载插件
  • 简单的API设计
  • 线程安全

基本使用方法

添加依赖

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

[dependencies]
upon = "0.5"

定义插件接口

use upon::{Plugin, PluginManager};

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

实现插件

struct EnglishGreeter;

impl Plugin for EnglishGreeter {
    fn name(&self) -> &str {
        "english_greeter"
    }
}

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

struct SpanishGreeter;

impl Plugin for SpanishGreeter {
    fn name(&self) -> &str {
        "spanish_greeter"
    }
}

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

使用插件管理器

fn main() {
    // 创建插件管理器
    let mut manager = PluginManager::new();
    
    // 注册插件
    manager.register(Box::new(EnglishGreeter));
    manager.register(Box::new(SpanishGreeter));
    
    // 使用插件
    for plugin in manager.get_all::<dyn GreeterPlugin>() {
        println!("{} says: {}", plugin.name(), plugin.greet("Rust"));
    }
    
    // 输出:
    // english_greeter says: Hello, Rust!
    // spanish_greeter says: ¡Hola, Rust!
}

高级用法

动态加载插件

upon支持从共享库(.so/.dll)动态加载插件:

use upon::DynamicPlugin;

fn main() {
    let mut manager = PluginManager::new();
    
    // 从动态库加载插件
    unsafe {
        manager.load_dynamic("path/to/plugin.so").unwrap();
    }
    
    // 使用动态加载的插件...
}

插件配置

upon支持为插件提供配置:

use upon::{Plugin, PluginConfig};

struct ConfigurablePlugin {
    config: PluginConfig,
}

impl Plugin for ConfigurablePlugin {
    fn name(&self) -> &str {
        "configurable"
    }
    
    fn configure(&mut self, config: PluginConfig) {
        self.config = config;
    }
}

插件依赖管理

fn main() {
    let mut manager = PluginManager::new();
    
    // 设置插件依赖关系
    manager.set_dependency("plugin_a", &["plugin_b"]);
    
    // 初始化时会确保依赖顺序正确
    manager.init().unwrap();
}

实际应用示例

下面是一个更完整的示例,展示如何在实际项目中使用upon:

use upon::{Plugin, PluginManager};

// 定义数据库操作插件接口
trait DatabasePlugin: Plugin {
    fn connect(&self, connection_string: &str) -> Result<(), String>;
    fn query(&self, sql: &str) -> Result<Vec<String>, String>;
}

// 实现PostgreSQL插件
struct PostgresPlugin;

impl Plugin for PostgresPlugin {
    fn name(&self) -> &str {
        "postgres"
    }
}

impl DatabasePlugin for PostgresPlugin {
    fn connect(&self, connection_string: &str) -> Result<(), String> {
        println!("Connecting to PostgreSQL: {}", connection_string);
        Ok(())
    }
    
    fn query(&self, sql: &str) -> Result<Vec<String>, String> {
        println!("Executing PostgreSQL query: {}", sql);
        Ok(vec!["result1".to_string(), "result2".to_string()])
    }
}

// 实现SQLite插件
struct SqlitePlugin;

impl Plugin for SqlitePlugin {
    fn name(&self) -> &str {
        "sqlite"
    }
}

impl DatabasePlugin for SqlitePlugin {
    fn connect(&self, connection_string: &str) -> Result<(), String> {
        println!("Connecting to SQLite: {}", connection_string);
        Ok(())
    }
    
    fn query(&self, sql: &str) -> Result<Vec<String>, String> {
        println!("Executing SQLite query: {}", sql);
        Ok(vec!["result1".to_string()])
    }
}

fn main() {
    let mut manager = PluginManager::new();
    
    // 注册数据库插件
    manager.register(Box::new(PostgresPlugin));
    manager.register(Box::new(SqlitePlugin));
    
    // 根据配置选择插件
    let db_type = "postgres"; // 可以从配置读取
    
    if let Some(db_plugin) = manager.get::<dyn DatabasePlugin>(db_type) {
        db_plugin.connect("host=localhost dbname=mydb").unwrap();
        let results = db_plugin.query("SELECT * FROM users").unwrap();
        println!("Query results: {:?}", results);
    else {
        eprintln!("No database plugin found for type: {}", db_type);
    }
}

upon库为Rust应用程序提供了强大而灵活的插件系统,使得应用程序可以轻松扩展功能而无需修改核心代码。通过定义清晰的插件接口和利用upon的插件管理功能,开发者可以构建高度模块化和可扩展的应用程序。

完整示例demo

以下是一个完整的upon插件系统示例,展示了从定义到使用的完整流程:

use upon::{Plugin, PluginManager};

// 1. 定义插件接口
trait CalculatorPlugin: Plugin {
    fn add(&self, a: i32, b: i32) -> i32;
    fn subtract(&self, a: i32, b: i32) -> i32;
}

// 2. 实现基础计算器插件
struct BasicCalculator;

impl Plugin for BasicCalculator {
    fn name(&self) -> &str {
        "basic_calculator"
    }
}

impl CalculatorPlugin for BasicCalculator {
    fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }
    
    fn subtract(&self, a: i32, b: i32) -> i32 {
        a - b
    }
}

// 3. 实现科学计算器插件
struct ScientificCalculator;

impl Plugin for ScientificCalculator {
    fn name(&self) -> &str {
        "scientific_calculator"
    }
}

impl CalculatorPlugin for ScientificCalculator {
    fn add(&self, a: i32, b: i32) -> i32 {
        // 使用更复杂的算法作为示例
        (a as f64 + b as f64).round() as i32
    }
    
    fn subtract(&self, a: i32, b: i32) -> i32 {
        // 使用更复杂的算法作为示例
        (a as f64 - b as f64).round() as i32
    }
}

// 4. 实现带配置的计算器插件
use upon::{PluginConfig, Value};

struct ConfigurableCalculator {
    rounding: bool,
}

impl Plugin for ConfigurableCalculator {
    fn name(&self) -> &str {
        "configurable_calculator"
    }
    
    fn configure(&mut self, config: PluginConfig) {
        if let Some(Value::Bool(rounding)) = config.get("rounding") {
            self.rounding = *rounding;
        }
    }
}

impl CalculatorPlugin for ConfigurableCalculator {
    fn add(&self, a: i32, b: i32) -> i32 {
        if self.rounding {
            ((a + b) as f64 / 10.0).round() as i32 * 10
        } else {
            a + b
        }
    }
    
    fn subtract(&self, a: i32, b: i32) -> i32 {
        if self.rounding {
            ((a - b) as f64 / 10.0).round() as i32 * 10
        } else {
            a - b
        }
    }
}

fn main() {
    // 创建插件管理器
    let mut manager = PluginManager::new();
    
    // 注册插件
    manager.register(Box::new(BasicCalculator));
    manager.register(Box::new(ScientificCalculator));
    
    // 创建并配置可配置计算器
    let mut configurable = ConfigurableCalculator { rounding: false };
    let mut config = PluginConfig::new();
    config.insert("rounding", true);
    configurable.configure(config);
    manager.register(Box::new(configurable));
    
    // 使用所有插件进行计算
    let a = 23;
    let b = 17;
    
    for plugin in manager.get_all::<dyn CalculatorPlugin>() {
        println!("{} results:", plugin.name());
        println!("  {} + {} = {}", a, b, plugin.add(a, b));
        println!("  {} - {} = {}", a, b, plugin.subtract(a, b));
    }
    
    // 根据名称获取特定插件
    if let Some(calc) = manager.get::<dyn CalculatorPlugin>("basic_calculator") {
        println!("Basic calculator special result: {}", calc.add(100, 200));
    }
}

这个完整示例展示了:

  1. 如何定义插件接口
  2. 实现不同类型的插件(基础、科学、可配置)
  3. 如何使用插件配置系统
  4. 如何注册和使用多个插件
  5. 如何按名称获取特定插件
回到顶部