3 releases

0.1.2 Sep 24, 2024
0.1.1 Sep 23, 2024
0.1.0 Sep 22, 2024

#1750 in Web programming

21 downloads per month

MIT license

5.5MB
268 lines

Contains (ELF lib, 3.5MB) plugins/arp-skeleton/libarp_skeleton.so, (ELF lib, 3.5MB) libarp_skeleton.so, (ELF lib, 1.5MB) libarp_gmail.so, (ELF lib, 1.5MB) plugins/arp-gmail/libarp_gmail.so, (ELF lib, 570KB) libarp_foo_bar.so, (ELF lib, 570KB) plugins/foo-bar/libarp_foo_bar.so

axum-router-plugin

Axum Router Plugin - Dynamically loadable libraries and routes

Rust

Attention: This project is in an experimental stage and may contain bugs or limitations. It has only been tested in a Linux environment.

Description

Early-stage, experimental Rust project that allows developers to dynamically load and unload shared libraries, similar to enabling or disabling plugins in WordPress. This flexibility enables developers to extend Axum web applications without recompiling the entire application. The system automatically generates routes for library functions and supports integration with template engines like Tera. A simple configuration file manages the loaded libraries, providing flexibility and extensibility for building custom web applications.

Just like in WordPress, plugins reside in the plugins directory, each with its own configuration. Plugins may also include templates that can be used in the main application’s templates via the Tera include keyword.

<div>
  {% include "plugins/arp-gmail/templates/form.html" %}
</div>

Usage Example:

use axum_router_plugin::Plugins;
use axum::{
  routing::get,
  Router,
};

#[tokio::main]
async fn main() {
  // Load plugins from the plugins directory.
  // Each plugin must have its own directory containing a plugin.json file
  // that provides information about the plugin, such as the library path,
  // version, and whether it's enabled.
  //
  // You can change the location of the plugins directory by setting
  // the environment variable PLUGINS_DIR, for example:
  // export PLUGINS_DIR=path/to/plugins
  //
  // Set the argument to true if you want to add the plugin name to the routes.
  let axum_plugins = Plugins::new(Some(true));

  // Load the plugins and create a router with the loaded plugins.
  // If loading fails, the program will panic with an error message.
  let plugins_router = match axum_plugins.load() {
    Ok(router) => router,
    Err(err) => panic!("Error loading plugins: {}", err),
  };

  // Build our application with a route.
  // The plugins are nested under the "/plugin" path.
  let _app = Router::new()
    .route("/", get(|| async {
      "Hello world!"
    }))
    .nest("/plugin", plugins_router);
}

Plugin Configuration:

To load shared libraries, there must be a plugins directory. Each plugin inside the plugins directory must include a plugins.json file. This file specifies the library path, version, and whether the plugin is enabled.

Example plugins.json entry:

{
  "name": "plugin_name",
  "description": "Axum Router Plugin Example",
  "lib_path": "./path/to/plugin.so",
  "version": "0.1.0",
  "license": "MIT",
  "enabled": true
}

You can change the location of the plugins directory by setting the PLUGINS_DIR environment variable.

Example:

# Set the plugins directory
export PLUGINS_DIR=path/to/plugins
# Unset the environment variable in bash
unset PLUGINS_DIR
# Unset the environment variable in fish
set --erase PLUGINS_DIR

How to test the provided example:

git clone https://github.com/mrhdias/axum-router-plugin
cd axum-router-plugin
ls -la plugins
nano -w plugins/arp-skeleton/plugin.json
cargo run --example app

In the examples directory, there is a templates directory that demonstrates how plugin routes can be used with shortcodes to display content provided by plugins. The shortcodes are available through the Tera template engine.

Usage Example:

{{ plugin(route="/plugin/foo-bar/test-get", method='get', jscaller="true") | safe }}

{% set my_vegetables = '["carrot", "potato", "tomato", "beet"]' %}
{% set my_bag = '{
    "fruits": ["apple", "orange", "banana"],
    "vegetables": ' ~ my_vegetables ~ '
}' %}
Data from plugin function: 
<pre>
{{ plugin(route="/plugin/foo-bar/test-json", method='post', data=my_bag, jscaller="true") | safe }}
</pre>

Plugin Examples

For more information about the plugins, refer to the plugin skeleton:

git clone https://github.com/mrhdias/arp-skeleton
cd arp-skeleton
cargo build --release
cp target/release/libarp_skeleton.so ../axum-router-plugin/plugins/arp-skeleton
cp plugin.json ../axum-router-plugin/plugins/arp-skeleton

Another plugin example:

git clone https://github.com/mrhdias/arp-foo-bar
cd arp-foo-bar
cargo build --release
cp target/release/libarp_foo_bar.so ../axum-router-plugin/plugins/foo-bar
cp plugin.json ../axum-router-plugin/plugins/foo-bar

Shared libraries must implement a routes function that returns a JSON array containing all available routes for the library.

Example JSON:

[
  {
    "path": "/test-get",
    "function": "test_get",
    "method_router": "get",
    "response_type": "html"
  },
  {
    "path": "/test-post",
    "function": "test_post",
    "method_router": "post",
    "response_type": "html"
  },
  {
    "path": "/test-json",
    "function": "test_json",
    "method_router": "post",
    "response_type": "json"
  },
  {
    "path": "/version",
    "function": "version",
    "method_router": "get",
    "response_type": "text"
  }
]

Dependencies

~6–13MB
~149K SLoC