alma 0.1.1

A Bevy-native modal text editor with Vim-style navigation.
Documentation
//! Host import authorization context.

use super::{AuthorizedWorkspaceReadAccess, AuthorizedWorkspaceWriteAccess};
use crate::plugin::{
    PluginAuthorizationError, PluginCapabilities, PluginCapabilityRef, PluginIdentity,
    WorkspacePath, WorkspacePathRef,
};
use std::fmt::{Debug, Formatter};

/// Plugin-scoped authority captured from effective static policy.
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::plugin) struct PluginHostAuthority {
    /// Stable plugin identity.
    identity: PluginIdentity,
    /// Effective grants after config and manifest intersection.
    capabilities: PluginCapabilities,
}

impl PluginHostAuthority {
    /// Pairs stable identity with effective grants.
    #[must_use]
    const fn new(identity: PluginIdentity, capabilities: PluginCapabilities) -> Self {
        Self {
            identity,
            capabilities,
        }
    }

    /// Returns the validated identity proof.
    #[must_use]
    pub const fn identity_proof(&self) -> &PluginIdentity {
        &self.identity
    }

    /// Returns the effective capability set.
    #[must_use]
    pub const fn capabilities(&self) -> &PluginCapabilities {
        &self.capabilities
    }

    /// Authorizes a host import capability request.
    ///
    /// # Errors
    ///
    /// Returns [`PluginAuthorizationError`] when the grant is absent.
    pub fn authorize(
        &self,
        capability: PluginCapabilityRef<'_>,
    ) -> Result<(), PluginAuthorizationError> {
        if self.capabilities.allows(capability) {
            Ok(())
        } else {
            Err(PluginAuthorizationError::Denied {
                identity: self.identity.clone(),
                capability: capability.shape(),
            })
        }
    }

    /// Authorizes a workspace observation request.
    ///
    /// # Errors
    ///
    /// Returns [`PluginAuthorizationError`] when no covering observation grant exists.
    pub(in crate::plugin) fn authorize_workspace_observe(
        &self,
        path: WorkspacePathRef<'_>,
    ) -> Result<AuthorizedWorkspaceReadAccess, PluginAuthorizationError> {
        self.authorize(PluginCapabilityRef::WorkspaceObserve(path))?;
        Ok(AuthorizedWorkspaceReadAccess::new(
            self.identity.clone(),
            WorkspacePath::from_ref(path),
        ))
    }

    /// Authorizes a workspace artifact-write request.
    ///
    /// # Errors
    ///
    /// Returns [`PluginAuthorizationError`] when no covering artifact-write grant exists.
    pub(in crate::plugin) fn authorize_workspace_artifact_write(
        &self,
        path: WorkspacePathRef<'_>,
    ) -> Result<AuthorizedWorkspaceWriteAccess, PluginAuthorizationError> {
        self.authorize(PluginCapabilityRef::WorkspaceArtifactWrite(path))?;
        Ok(AuthorizedWorkspaceWriteAccess::new(
            self.identity.clone(),
            WorkspacePath::from_ref(path),
        ))
    }
}

/// Host authorization context for one loaded plugin.
#[derive(Eq, PartialEq)]
pub struct PluginHostContext {
    /// Policy authority for host imports.
    authority: PluginHostAuthority,
}

impl PluginHostContext {
    /// Pairs stable identity with effective grants.
    #[must_use]
    pub(in crate::plugin) const fn new(
        identity: PluginIdentity,
        capabilities: PluginCapabilities,
    ) -> Self {
        Self {
            authority: PluginHostAuthority::new(identity, capabilities),
        }
    }

    /// Builds a host context for cross-module tests.
    #[cfg(test)]
    #[must_use]
    pub(crate) fn for_test(identity: impl Into<String>, capabilities: PluginCapabilities) -> Self {
        Self::new(
            PluginIdentity::try_new(identity).expect("test plugin identity should be valid"),
            capabilities,
        )
    }

    /// Returns the plugin identity.
    #[must_use]
    pub fn identity(&self) -> &str {
        self.identity_proof().as_str()
    }

    /// Returns the validated identity proof.
    #[must_use]
    pub const fn identity_proof(&self) -> &PluginIdentity {
        self.authority.identity_proof()
    }

    /// Returns the effective capability set.
    #[must_use]
    pub const fn capabilities(&self) -> &PluginCapabilities {
        self.authority.capabilities()
    }

    /// Returns the shared authority proof.
    #[must_use]
    pub(in crate::plugin) const fn authority(&self) -> &PluginHostAuthority {
        &self.authority
    }

    /// Authorizes a host import capability request.
    ///
    /// # Errors
    ///
    /// Returns [`PluginAuthorizationError`] when the grant is absent.
    pub fn authorize(
        &self,
        capability: PluginCapabilityRef<'_>,
    ) -> Result<(), PluginAuthorizationError> {
        self.authority.authorize(capability)
    }

    /// Authorizes a workspace observation request.
    ///
    /// # Errors
    ///
    /// Returns [`PluginAuthorizationError`] when no covering observation grant exists.
    pub fn authorize_workspace_observe(
        &self,
        path: WorkspacePathRef<'_>,
    ) -> Result<AuthorizedWorkspaceReadAccess, PluginAuthorizationError> {
        self.authority.authorize_workspace_observe(path)
    }

    /// Authorizes a workspace artifact-write request.
    ///
    /// # Errors
    ///
    /// Returns [`PluginAuthorizationError`] when no covering artifact-write grant exists.
    pub fn authorize_workspace_artifact_write(
        &self,
        path: WorkspacePathRef<'_>,
    ) -> Result<AuthorizedWorkspaceWriteAccess, PluginAuthorizationError> {
        self.authority.authorize_workspace_artifact_write(path)
    }
}

impl Debug for PluginHostContext {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        formatter
            .debug_struct("PluginHostContext")
            .field("identity", &self.identity_proof())
            .field("capabilities", self.capabilities())
            .finish()
    }
}