cordis-core 0.0.4

A typed, scope-based plugin runtime inspired by Cordis
Documentation
use std::{
    any::TypeId,
    marker::PhantomData,
    sync::{Arc, Weak},
};

use crate::{Result, runtime::Runtime};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ServiceId {
    pub key: TypeId,
    pub isolation: u64,
}

impl ServiceId {
    pub fn root(key: TypeId) -> Self {
        Self { key, isolation: 0 }
    }
}

/// Typed service key. The associated value may be unsized, including a trait
/// object such as `dyn Database`.
pub trait ServiceKey: Send + Sync + 'static {
    type Value: ?Sized + Send + Sync + 'static;
    const NAME: &'static str;
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ServiceDeclaration {
    pub(crate) key: TypeId,
    pub name: &'static str,
}

impl ServiceDeclaration {
    pub fn of<K: ServiceKey>() -> Self {
        Self {
            key: TypeId::of::<K>(),
            name: K::NAME,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Dependency {
    pub(crate) key: TypeId,
    pub name: &'static str,
    pub required: bool,
}

impl Dependency {
    pub fn required<K: ServiceKey>() -> Self {
        Self {
            key: TypeId::of::<K>(),
            name: K::NAME,
            required: true,
        }
    }

    pub fn optional<K: ServiceKey>() -> Self {
        Self {
            key: TypeId::of::<K>(),
            name: K::NAME,
            required: false,
        }
    }
}

/// Non-owning service control handle. Dropping it does not unregister the
/// service; ownership remains with the activation scope.
pub struct ServiceHandle<K: ServiceKey> {
    pub(crate) runtime: Weak<Runtime>,
    pub(crate) id: ServiceId,
    pub(crate) owner: u64,
    pub(crate) token: u64,
    pub(crate) _key: PhantomData<fn() -> K>,
}

impl<K: ServiceKey> ServiceHandle<K> {
    pub fn replace(&self, value: Arc<K::Value>) -> Result<u64> {
        let runtime = self.runtime.upgrade().ok_or(crate::Error::PluginDisposed)?;
        runtime.replace_service::<K>(self.id, self.owner, self.token, value)
    }

    pub fn touch(&self) -> Result<u64> {
        let runtime = self.runtime.upgrade().ok_or(crate::Error::PluginDisposed)?;
        runtime.touch_service::<K>(self.id, self.owner, self.token)
    }

    /// Unregisters early. Safe to call repeatedly.
    pub fn remove(&self) -> bool {
        self.runtime
            .upgrade()
            .is_some_and(|runtime| runtime.remove_service(self.id, self.owner, self.token))
    }
}

pub(crate) struct ServiceEntry {
    pub value: Box<dyn std::any::Any + Send + Sync>,
    pub owner: u64,
    pub token: u64,
    pub generation: u64,
    pub name: &'static str,
    pub active: bool,
}

pub(crate) fn boxed_service<K: ServiceKey>(
    value: Arc<K::Value>,
) -> Box<dyn std::any::Any + Send + Sync> {
    Box::new(value)
}