rust - 是否有可能像 Rust 中的 WordPress 操作一样制作插件扩展 Hook ?

标签 rust

我即将用 Rust 重写一个高度模块化的 CMS,所以我的问题是是否有可能让“核心”应用程序设置扩展点(操作/ Hook ),其他插件/ crate 能够“选项卡”进入。

Something like this就足够了,但是你如何在 Rust 中做到这一点?上面的体系结构使用插件注册表并通过迭代每个插件从核心启动每个插件的主要方法。但是在 Rust 中,因为你不能在例如一个 plugin_registry lib crate,我想这不是 Rust 中的正确想法。

是否有更好、更灵活的方法使“插件”与核心应用程序无缝集成?例如,像 WordPress 使用的事件调度程序之类的东西?

最佳答案

正如 Shepmaster 所说,这是一个非常普遍的问题;因此有很多方法可以做你想做的事。正如已经提到的,iron是模块化框架的一个很好的例子。

但是,我将尝试提供一个有用的示例来说明如何 实现这样一个插件系统。对于我假设的示例,有某种主箱可以加载插件并“配置”CMS。这意味着插件不是动态加载的!


结构

首先,假设我们有四个 crate :

  • rustpress:具有所有类似 WordPress 功能的大型主箱
  • rustpress-plugin:插件作者需要使用(是一个自己的 crate,以避免为每个插件使用像 rustpress 这样的巨大 crate)
  • rustpress-signature:在这里我们创建我们的插件,它将为每个帖子添加签名
  • my-blog:这将是配置我们博客的主要可执行文件,稍后将作为 Web 服务器运行

1。特征/接口(interface)

Rust 的方法是 trait。您可以将它们与其他语言的界面进行比较。我们现在将为位于 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_* 方法已有不执行任何操作的默认实现 ({})。这意味着如果插件只想使用一个 Hook ,则不必覆盖所有方法。

2。编写我们的插件

正如我所说,我们想要编写一个插件,将我们的签名添加到每个帖子正文中。为此,我们将为自己的类型impl trait(在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(我们需要将它们装箱,因为我们想要所有权,但 traits 没有大小)。当 CMS 然后准备博客文章时,它会遍历所有插件并调用所有 Hook 。

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 ,也是。

关于rust - 是否有可能像 Rust 中的 WordPress 操作一样制作插件扩展 Hook ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34968488/

相关文章:

unicode - 我如何在 Rust 中折叠字符串?

reference - Rust 找不到特征实现

enums - 我如何构造/解构枚举的枚举?

rust - 理解错误 "Borrowed value does not live long enough"

rust - 为什么 Rust 中的泛型[有时]不需要生命周期说明符?

asynchronous - 串的Rust future

vector - 如何为向量的迭代器元素添加 Into 的类型注释?

rust - 为什么派生的 clone() 方法返回一个引用?

docker - 使用 Rust 访问 google cloud run (docker) 中的环境变量的适当方法

rust - 如何隐藏子模块中的方法但仍可从父模块中使用它?