cordis-core 0.0.4

A typed, scope-based plugin runtime inspired by Cordis
Documentation
/// Library-wide result type.
pub type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("service `{name}` is not registered")]
    MissingService { name: &'static str },

    #[error("service `{name}` is already registered in this scope")]
    DuplicateService { name: &'static str },

    #[error("service `{name}` was stored with an incompatible type")]
    ServiceTypeMismatch { name: &'static str },

    #[error("service `{name}` is owned by another activation")]
    ServiceOwnership { name: &'static str },

    #[error("scope `{name}` is no longer active")]
    ScopeInactive { name: String },

    #[error("application is shut down")]
    ApplicationShutdown,

    #[error("plugin is disposed")]
    PluginDisposed,

    #[error("plugin failed: {0}")]
    PluginFailed(String),

    #[error("task failed: {0}")]
    TaskJoin(#[from] tokio::task::JoinError),

    #[error("task did not stop within {seconds} seconds")]
    TaskTimeout { seconds: u64 },

    #[error("panic captured: {0}")]
    Panic(String),

    #[error("dependency cycle: {0}")]
    DependencyCycle(String),

    #[error("cleanup failed: {0}")]
    Cleanup(String),

    #[error(transparent)]
    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}

impl Error {
    pub fn cleanup(error: impl std::fmt::Display) -> Self {
        Self::Cleanup(error.to_string())
    }

    pub(crate) fn panic(payload: Box<dyn std::any::Any + Send>) -> Self {
        let message = payload
            .downcast_ref::<&str>()
            .map(|value| (*value).to_owned())
            .or_else(|| payload.downcast_ref::<String>().cloned())
            .unwrap_or_else(|| "non-string panic payload".to_owned());
        Self::Panic(message)
    }
}