oxidite-plugin

Crates.iooxidite-plugin
lib.rsoxidite-plugin
version2.0.1
created_at2026-01-21 18:43:57.673654+00
updated_at2026-01-25 01:54:17.275258+00
descriptionPlugin system for Oxidite v2 web framework with dynamic loading and extension capabilities
homepage
repositoryhttps://github.com/meshackbahati/rust-oxidite
max_upload_size
id2059862
size80,720
Meshack Bahati Ouma (meshackbahati)

documentation

README

oxidite-plugin

Plugin system for the Oxidite web framework.

Crates.io Docs.rs License

Overview

oxidite-plugin provides a flexible plugin system that enables extending Oxidite applications with additional functionality. The plugin architecture supports dynamic loading, lifecycle management, and hook-based extension points. Plugins can add routes, middleware, database migrations, and more to your Oxidite application.

Installation

Add this to your Cargo.toml:

[dependencies]
oxidite-plugin = "2.0"
oxidite = "2.0"

Features

  • Dynamic Plugin Loading: Load plugins at runtime without recompilation
  • Hook System: Extensive hook points for intercepting application lifecycle events
  • Dependency Management: Plugin dependency resolution and version compatibility
  • Secure Sandboxing: Isolated plugin execution environment
  • Hot Reloading: Plugin updates without application restart
  • Configuration Integration: Plugin-specific configuration management
  • Event Broadcasting: Inter-plugin communication via events
  • Asset Serving: Plugin-specific static assets and resources

Usage

Creating a Basic Plugin

use oxidite_plugin::{Plugin, PluginInfo, HookRegistry, PluginResult};
use oxidite::prelude::*;

pub struct HelloWorldPlugin;

impl Plugin for HelloWorldPlugin {
    fn info(&self) -> PluginInfo {
        PluginInfo {
            name: "hello_world".to_string(),
            version: "1.0.0".to_string(),
            author: "Developer".to_string(),
            description: "A simple hello world plugin".to_string(),
        }
    }

    fn register_hooks(&self, hooks: &mut HookRegistry) -> PluginResult<()> {
        // Register plugin hooks
        hooks.register("before_startup", |ctx| {
            println!("HelloWorldPlugin: Running before startup");
            Ok(())
        })?;
        
        hooks.register("after_startup", |ctx| {
            println!("HelloWorldPlugin: Running after startup");
            Ok(())
        })?;
        
        Ok(())
    }

    fn init(&self, app: &mut Router) -> PluginResult<()> {
        // Add routes to the application
        app.get("/hello", |_: Request| async {
            Ok(response::text("Hello from plugin!"))
        });
        
        Ok(())
    }
}

// Register the plugin
#[tokio::main]
async fn main() -> Result<()> {
    let mut router = Router::new();
    
    // Load and initialize the plugin
    let plugin = HelloWorldPlugin;
    plugin.init(&mut router)?;
    
    Server::new(router)
        .listen("127.0.0.1:3000".parse().unwrap())
        .await
}

Plugin with Configuration

use oxidite_plugin::{Plugin, PluginInfo, PluginResult};
use serde::Deserialize;

#[derive(Deserialize, Clone)]
struct HelloConfig {
    greeting: String,
    target: String,
}

pub struct ConfigurableHelloPlugin {
    config: HelloConfig,
}

impl ConfigurableHelloPlugin {
    pub fn new(config: HelloConfig) -> Self {
        Self { config }
    }
}

impl Plugin for ConfigurableHelloPlugin {
    fn info(&self) -> PluginInfo {
        PluginInfo {
            name: "configurable_hello".to_string(),
            version: "1.0.0".to_string(),
            author: "Developer".to_string(),
            description: "A configurable hello plugin".to_string(),
        }
    }

    fn init(&self, app: &mut Router) -> PluginResult<()> {
        let greeting = self.config.greeting.clone();
        let target = self.config.target.clone();
        
        app.get("/custom-hello", move |_: Request| {
            let greeting = greeting.clone();
            let target = target.clone();
            async move {
                Ok(response::text(format!("{} {}!", greeting, target)))
            }
        });
        
        Ok(())
    }
}

Plugin with Database Operations

use oxidite_plugin::{Plugin, PluginInfo, PluginResult};
use oxidite_db::{Model, Database};

#[derive(Model)]
#[model(table = "plugin_data")]
struct PluginData {
    #[model(primary_key)]
    id: i32,
    key: String,
    value: String,
}

pub struct DatabasePlugin {
    db: Database,
}

impl DatabasePlugin {
    pub fn new(db: Database) -> Self {
        Self { db }
    }
}

impl Plugin for DatabasePlugin {
    fn info(&self) -> PluginInfo {
        PluginInfo {
            name: "database_plugin".to_string(),
            version: "1.0.0".to_string(),
            author: "Developer".to_string(),
            description: "A plugin with database operations".to_string(),
        }
    }

    fn init(&self, app: &mut Router) -> PluginResult<()> {
        // Add routes that use database
        app.get("/plugin-data", {
            let db = self.db.clone();
            move |_: Request| {
                let db = db.clone();
                async move {
                    let data: Vec<PluginData> = db.find_all()?;
                    Ok(response::json(serde_json::json!(data)))
                }
            }
        });
        
        Ok(())
    }
}

Hook System

The plugin system provides various hooks for different lifecycle events:

Application Lifecycle Hooks

use oxidite_plugin::{HookRegistry, PluginContext};

fn register_app_lifecycle_hooks(hooks: &mut HookRegistry) -> PluginResult<()> {
    // Called before application starts
    hooks.register("before_startup", |ctx: &PluginContext| {
        println!("Application starting...");
        Ok(())
    })?;
    
    // Called after application starts
    hooks.register("after_startup", |ctx: &PluginContext| {
        println!("Application started successfully");
        Ok(())
    })?;
    
    // Called before application shuts down
    hooks.register("before_shutdown", |ctx: &PluginContext| {
        println!("Application shutting down...");
        Ok(())
    })?;
    
    Ok(())
}

Request Processing Hooks

fn register_request_hooks(hooks: &mut HookRegistry) -> PluginResult<()> {
    // Called before request processing
    hooks.register("before_request", |ctx: &PluginContext| {
        let req = ctx.request()?;
        println!("Processing request: {} {}", req.method(), req.uri());
        Ok(())
    })?;
    
    // Called after response generation
    hooks.register("after_response", |ctx: &PluginContext| {
        let resp = ctx.response()?;
        println!("Response status: {}", resp.status());
        Ok(())
    })?;
    
    Ok(())
}

Route Registration Hooks

fn register_route_hooks(hooks: &mut HookRegistry) -> PluginResult<()> {
    // Called when routes are being registered
    hooks.register("before_route_registration", |ctx: &PluginContext| {
        // Modify route registration behavior
        Ok(())
    })?;
    
    Ok(())
}

Plugin Manager

The plugin manager handles loading, initialization, and management of plugins:

use oxidite_plugin::{PluginManager, PluginLoader};

#[tokio::main]
async fn main() -> Result<()> {
    let mut router = Router::new();
    let mut plugin_manager = PluginManager::new();
    
    // Load plugins from configuration
    plugin_manager
        .load_plugin(Box::new(HelloWorldPlugin))
        .await?;
    
    // Initialize all plugins
    plugin_manager.initialize(&mut router).await?;
    
    Server::new(router)
        .listen("127.0.0.1:3000".parse().unwrap())
        .await
}

Advanced Features

Plugin Dependencies

use oxidite_plugin::{Plugin, PluginDependencies};

impl Plugin for MyPlugin {
    fn dependencies(&self) -> PluginDependencies {
        PluginDependencies::new()
            .requires("auth_plugin", ">=1.0.0")
            .optional("logging_plugin", ">=1.0.0")
    }
}

Asset Serving

impl Plugin for AssetPlugin {
    fn assets(&self) -> Option<AssetProvider> {
        Some(AssetProvider::new()
            .add_directory("static", "./assets")
            .add_file("logo.png", "./assets/logo.png"))
    }
}

Event System

// Emit events from plugins
hooks.emit("user_created", &UserData { id: 1, name: "John" })?;

// Listen for events
hooks.on("user_created", |data: &UserData| {
    println!("User created: {}", data.name);
    Ok(())
})?;

Configuration Schema

use oxidite_plugin::ConfigSchema;

impl Plugin for ConfigurablePlugin {
    fn config_schema(&self) -> Option<ConfigSchema> {
        Some(ConfigSchema::new()
            .field("api_key", "API key for external service", "string")
            .field("timeout", "Request timeout in seconds", "integer")
            .field("enabled", "Enable plugin functionality", "boolean"))
    }
}

Security

  • Sandboxed Execution: Plugins run in a restricted environment
  • Permission System: Fine-grained permission controls for plugin capabilities
  • Isolation: Plugins are isolated from each other and the core application
  • Validation: Input validation for all plugin-provided data

Performance

  • Lazy Loading: Plugins are loaded only when needed
  • Caching: Cached plugin metadata and configurations
  • Efficient Hooks: Optimized hook execution with minimal overhead
  • Resource Management: Proper cleanup of plugin resources

Best Practices

  1. Keep Plugins Focused: Each plugin should have a single, well-defined purpose
  2. Use Configuration: Make plugins configurable rather than hardcoded
  3. Handle Errors Gracefully: Ensure plugins fail gracefully without affecting the main application
  4. Document Hooks: Clearly document which hooks your plugin uses and provides
  5. Version Dependencies: Specify compatible versions for plugin dependencies

License

MIT

Commit count: 42

cargo fmt