正如 Shepmaster 所说,这是一个非常笼统的问题;因此有很多方法可以做你想做的事。正如已经提到的,iron 是模块化框架的一个很好的例子。
但是,我将尝试给出一个有用的示例,说明如何 实现这样的插件系统。对于我将假设的示例,有某种 main-crate 可以加载插件并“配置”CMS。这意味着插件不是动态加载的!
结构
首先,假设我们有四个箱子:
-
rustpress:具有所有类似 WordPress 功能的大型主箱
-
rustpress-plugin:需要插件作者使用(是一个自己的 crate,以避免为每个插件使用像 rustpress 这样的巨大 crate)
-
rustpress-signature:在这里我们创建了我们的插件,它将为每个帖子添加一个签名
-
my-blog:这将是配置我们博客的主要可执行文件,稍后将作为 Web 服务器运行
1。特征/界面
使用 Rust 的方法是 traits。您可以将它们与其他语言的界面进行比较。我们现在将为位于rustpress-plugin 中的插件设计trait:
pub trait Plugin {
/// Returns the name of the plugin
fn name(&self) -> &str;
/// Hook to change the title of a post
fn filter_title(&self, title: &mut String) {}
/// Hook to change the body of a post
fn filter_body(&self, body: &mut String) {}
}
请注意,filter_* 方法已经有一个什么都不做的默认实现 ({})。这意味着插件如果只想使用一个钩子,就不必重写所有方法。
2。编写我们的插件
正如我所说,我们想编写一个插件,将我们的签名添加到每个帖子正文中。为此,我们将 impl 我们自己类型的特征(在 rustpress-signature 中):
extern crate rustpress_plugin;
use rustpress_plugin::Plugin;
pub struct Signature {
pub text: String,
}
impl Plugin for Signature {
fn name(&self) -> &str {
"Signature Plugin v0.1 by ferris"
}
fn filter_body(&self, body: &mut String) {
body.push_str("\n-------\n"); // add visual seperator
body.push_str(&self.text);
}
}
我们创建了一个简单的类型Signature,我们为它实现了特征Plugin。我们必须实现name() 方法,我们还覆盖filter_body() 方法。在我们的实现中,我们只是在帖子正文中添加文本。我们没有覆盖filter_title(),因为我们不需要。
3。实现插件栈
CMS 必须管理所有插件。我假设 CMS 有一个主要类型 RustPress 可以处理所有事情。它可能看起来像这样(rustpress):
extern crate rustpress_plugin;
use rustpress_plugin::Plugin;
pub struct RustPress {
// ...
plugins: Vec<Box<Plugin>>,
}
impl RustPress {
pub fn new() -> RustPress {
RustPress {
// ...
plugins: Vec::new(),
}
}
/// Adds a plugin to the stack
pub fn add_plugin<P: Plugin + 'static>(&mut self, plugin: P) {
self.plugins.push(Box::new(plugin));
}
/// Internal function that prepares a post
fn serve_post(&self) {
let mut title = "dummy".to_string();
let mut body = "dummy body".to_string();
for p in &self.plugins {
p.filter_title(&mut title);
p.filter_body(&mut body);
}
// use the finalized title and body now ...
}
/// Starts the CMS ...
pub fn start(&self) {}
}
我们在这里所做的是存储一个装满盒装插件的Vec(我们需要将它们装箱,因为我们想要所有权,但特征没有大小)。当 CMS 准备一篇博文时,它会遍历所有插件并调用所有钩子。
4。配置并启动 CMS
最后一步是添加插件并启动 CMS(将它们放在一起)。我们将在my-blog crate 中执行此操作:
extern crate rustpress;
extern crate rustpress_plugin;
extern crate rustpress_signature;
use rustpress::RustPress;
use rustpress_plugin::Plugin;
use rustpress_signature::Signature;
fn main() {
let mut rustpress = RustPress::new();
// add plugin
let sig = Signature { text: "Ferris loves you <3".into() };
rustpress.add_plugin(sig);
rustpress.start();
}
您还需要将依赖项添加到Cargo.toml 文件中。我省略了,因为它应该相当容易。
再次注意,这是创建这样一个系统的众多可能性中的之一。我希望这个例子是有帮助的。你也可以试试on playground。