Skip to content

Generic event-driven-plugins crate #277

@tristanpoland

Description

@tristanpoland

Issue: Overhaul universal_plugin_system to Enable App-Specific Event/Plugin Architectures

Goal

Transform the universal_plugin_system crate into a truly generic, reusable foundation that allows any application to implement its own event-driven plugin system—matching the power, flexibility, and ergonomics of Horizon’s current event and plugin architecture, but without any Horizon-specific logic or types.

Requirements

  • No App-Specific Logic:
    The crate must not contain any event types, handler signatures, or plugin logic specific to Horizon or any other app.

  • Extensible Event Domains:
    Support for registering and emitting events in arbitrary domains (e.g., core, client, plugin, custom), with ergonomic APIs for each, but without hardcoding any domain names.

  • Type-Safe, Async Handlers:
    Allow host apps to define their own event types and register async handlers with full type safety and minimal boilerplate.

  • Custom Propagators:
    Allow host apps to define and plug in their own event propagation strategies (e.g., local, networked, distributed), with the universal crate providing only the interfaces and hooks.

  • Plugin Lifecycle & Context:
    Provide a generic plugin trait (e.g., SimplePlugin) with async handler registration and context injection, but leave the context type open for the host app to define.

  • Minimal Boilerplate for Host Apps:
    Host apps should only need to define their event types, propagators, and context, and wire them up using the universal system’s traits and APIs.

  • Replaceability:
    The universal crate should be able to replace the generic functionality in Horizon’s event and plugin system, allowing Horizon to focus only on its domain logic.

Example Usage

Host App: Defining Events, Context, and Propagators

// In the host app
use universal_plugin_system::*;

#[derive(Debug, Clone)]
pub struct MyEvent { /* ... */ }

pub struct MyContext { /* ... */ }
impl ServerContext for MyContext {}

pub struct MyPropagator;
impl<E: Event> EventPropagator<E> for MyPropagator {
    fn propagate(&self, event: &E) { /* ... */ }
}

Host App: Setting Up the System

let context = Arc::new(MyContext { /* ... */ });
let event_system = UniversalEventSystem::new(context.clone(), MyPropagator);

event_system.on("core", "user_login", |event: MyEvent| async move {
    println!("User logged in: {:?}", event);
    Ok(())
}).await?;

Plugin Implementation

pub struct MyPlugin;

#[async_trait::async_trait]
impl SimplePlugin for MyPlugin {
    fn name(&self) -> &str { "my_plugin" }
    async fn register_handlers(
        &mut self,
        events: Arc<UniversalEventSystem>,
        context: Arc<dyn ServerContext>
    ) -> Result<(), PluginError> {
        events.on("client", "chat", |event: ChatEvent| async move {
            context.log(LogLevel::Info, &format!("Chat: {:?}", event));
            Ok(())
        }).await?;
        Ok(())
    }
}

Emitting Events

event_system.emit("core", "user_login", &MyEvent { /* ... */ }).await?;

Acceptance Criteria

  • Host apps can define all event types, domains, and propagators.
  • Plugins can register handlers for any event type/domain.
  • Plugin loading, lifecycle, and context are generic.
  • No app-specific logic or types in the universal crate.
  • Documentation and examples for host app and plugin authors.
  • This needs to be fully capable to the point where we can implement horizons system on top of it to eliminate the built in logic in horizon for all these tasks that art not horizon specific.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

Status

In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions