【问题标题】:Is it possible to make plugin extension hooks like WordPress actions in Rust?是否可以在 Rust 中制作类似于 WordPress 操作的插件扩展挂钩?
【发布时间】:2016-01-23 20:29:06
【问题描述】:

我即将用 Rust 重写一个高度模块化的 CMS,所以我的问题是是否有可能让“核心”应用程序设置扩展点(操作/挂钩),而其他插件/板条箱能够“制表符”。

Something like this 就足够了,但你将如何在 Rust 中做到这一点?上面的架构使用插件注册表并通过迭代每个插件从核心启动每个插件的 main 方法。但是在 Rust 中,因为你不能有一个全局的“模块”变量,例如一个 plugin_registry lib crate,我想这不是 Rust 的正确想法。

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

【问题讨论】:

  • Shepmaster 你为什么要否决并关闭它?很有建设性,我学到了很多。谢谢!
  • 当然是可能 - Rust 是图灵完备的等等。像iron 这样的东西有一个“中间件”堆栈——也许这会给你一个开始调查的地方?然后,您可以在 Stack Overflow 上提出具体的、有针对性的问题。
  • 我在问用扩展点编写核心应用程序的正确方法是什么,第 3 方模块/板条箱/插件可以从中挂钩并添加功能,无缝集成核心应用程序.. 很抱歉不是 rust 专家并提出了一个愚蠢的问题
  • 无意冒犯,我只是不相信当前形式的问题是on-topic。有各种各样的问题是not ideal to ask。不用担心,在问题真正结束之前还需要 4 人同意;也许他们会不同意我的观点。
  • 我会尝试重写我的问题。我对 rust 很陌生,但我真的很想知道制作这种插件结构的“rust 方式”

标签: rust


【解决方案1】:

正如 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

【讨论】:

  • 如果有些地方不够清楚或缺少某些信息,请告诉我!
  • 哇,终于。正是我正在寻找的 - 多么惊人的答案!非常感谢。尽管在阅读您的答案之前,我已经最终实现了稍微不同的架构。不过,我敢肯定,如果我将我的解决方案与您的解决方案结合起来,我将得到两者的最佳结果,并且可能接近最佳架构,所以再次感谢您抽出宝贵时间提出这个高质量的答案!跨度>
猜你喜欢
  • 2016-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-25
  • 2017-10-02
相关资源
最近更新 更多