Files
ruvnet--RuView/v2/crates/homecore-plugins/src/plugin.rs
T
ruv c04906e7a8 feat(homecore-plugins/p1): ADR-128 plugin runtime scaffold
Adds `v2/crates/homecore-plugins` (0.1.0-alpha.0) — the P1 scaffold for
the HOMECORE-PLUGINS WASM integration system (ADR-128):

- `manifest.rs`: `PluginManifest` — superset of HA manifest.json; serde
  round-trip + required-field validation (`domain`/`name`/`version`).
- `error.rs`: `PluginError` typed enum (InvalidManifest, AlreadyLoaded,
  NotFound, RuntimeError, SetupFailed, UnloadFailed, Io).
- `plugin.rs`: `HomeCorePlugin` async trait + `PluginId` newtype.
- `runtime.rs`: `PluginRuntime` trait + `InProcessRuntime` (native Rust,
  first-party plugins). `WasmtimeRuntime` stub gated on `--features wasmtime`
  (default-off; 30 MB dep deferred to P2).
- `registry.rs`: `PluginRegistry<R>` — load/unload/list/contains via RwLock.
- 10 unit tests, 0 failed.

Wasmtime vs wasm3 runtime selection is still open (ADR-128 §8 Q2);
this scaffold makes the choice swappable via the `PluginRuntime` trait.
The `wasmtime` and `wasm3` features are default-off; P2 resolves the choice
and wires host ABI (`hc_state_get`/`hc_state_set`/etc.) to ADR-127.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-25 18:13:53 -04:00

60 lines
2.0 KiB
Rust

//! `HomeCorePlugin` trait + `PluginId` newtype.
//!
//! Every first-party and third-party HOMECORE integration must implement
//! `HomeCorePlugin`. P1 provides an in-process native Rust implementation;
//! the WASM ABI wrapper (which maps the WASM exports `setup_entry`,
//! `call_service_handler`, `receive_event` to this trait) lands in P2.
use std::fmt;
use async_trait::async_trait;
use homecore::HomeCore;
use crate::error::PluginError;
/// Unique identifier for a loaded plugin — mirrors the `domain` field of
/// the plugin's `PluginManifest` (e.g. `"mqtt"`, `"homecore_lights"`).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PluginId(pub String);
impl PluginId {
/// Create a new `PluginId` from any string-like value.
pub fn new(s: impl Into<String>) -> Self {
Self(s.into())
}
/// Return the inner domain string.
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for PluginId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
/// Lifecycle trait that every HOMECORE integration must implement.
///
/// Implementing types are passed to [`PluginRuntime::load`]; the runtime
/// calls these methods at the appropriate lifecycle points.
///
/// # Async
/// Both methods are `async` to allow network / IO initialisation without
/// blocking the Tokio runtime. The `async_trait` macro erases the `impl`
/// return type so it works in trait objects.
#[async_trait]
pub trait HomeCorePlugin: Send + Sync + 'static {
/// Called once when the plugin's config entry is being set up.
///
/// The plugin receives a reference to the `HomeCore` runtime and should
/// register its entities, services, and event subscriptions here.
async fn setup(&self, hc: HomeCore) -> Result<(), PluginError>;
/// Called when the plugin is being removed from the registry.
///
/// The plugin should clean up subscriptions and deregister its entities.
async fn unload(&self) -> Result<(), PluginError>;
}