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));
}
}
这个完整示例展示了:
- 如何定义插件接口
- 实现不同类型的插件(基础、科学、可配置)
- 如何使用插件配置系统
- 如何注册和使用多个插件
- 如何按名称获取特定插件