use std::sync::Arc;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use crate::errors::PluginError;
use crate::manifest::PluginManifest;
use crate::registrar::PluginRegistrar;
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PluginId(SmolStr);
impl PluginId {
#[must_use]
pub fn new(s: impl Into<SmolStr>) -> Self {
let s = s.into();
assert!(!s.is_empty(), "PluginId must not be empty");
Self(s)
}
pub fn parse(s: impl AsRef<str>) -> Result<Self, PluginError> {
let s = s.as_ref();
if s.is_empty() {
return Err(PluginError::internal("PluginId must not be empty"));
}
Ok(Self(SmolStr::new(s)))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for PluginId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PluginHandle {
pub id: PluginId,
pub generation: u64,
}
impl PluginHandle {
#[must_use]
pub fn new(id: PluginId, generation: u64) -> Self {
Self { id, generation }
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct PluginInitContext<'a> {
pub effective_caps: &'a crate::CapabilitySet,
}
pub trait PluginState: Send + Sync + 'static {}
impl<T: Send + Sync + 'static> PluginState for T {}
pub trait Plugin: Send + Sync + 'static {
fn manifest(&self) -> &PluginManifest;
fn register(&self, r: &mut PluginRegistrar<'_>) -> Result<(), PluginError>;
fn init(&self, _cx: &PluginInitContext<'_>) -> Result<(), PluginError> {
Ok(())
}
fn shutdown(&self) {}
}
pub type DynPlugin = Arc<dyn Plugin>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn plugin_id_round_trip() {
let id = PluginId::new("ai.dragonscale.geo");
assert_eq!(id.as_str(), "ai.dragonscale.geo");
assert_eq!(id.to_string(), "ai.dragonscale.geo");
}
#[test]
#[should_panic(expected = "PluginId must not be empty")]
fn plugin_id_empty_panics() {
let _ = PluginId::new("");
}
#[test]
fn plugin_id_parse_rejects_empty() {
assert!(PluginId::parse("").is_err());
}
#[test]
fn plugin_handle_construction() {
let h = PluginHandle::new(PluginId::new("foo"), 0);
assert_eq!(h.id.as_str(), "foo");
assert_eq!(h.generation, 0);
}
}