alma 0.1.1

A Bevy-native modal text editor with Vim-style navigation.
Documentation
//! Layered host-import rejection types.

use super::super::{
    PluginHandleError, PluginHandleShape, PluginWorkspaceObserveTaskReserveError,
    effect::PluginEffectCommitError, workspace_io::PluginWorkspaceIoCommitError,
};
use super::resource::{PluginHostResourceKind, PluginResourceHandleRejectionReason};
use crate::plugin::{
    GuestDecodeError, PluginAuthorizationError, PluginIdentity, PluginOperationalImportRejection,
};
use std::fmt::{Debug, Display, Formatter};

/// Buffer observation rejection after authorization and handle decoding.
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum PluginBufferObservationError {
    /// The handle carries no captured buffer text snapshot.
    MissingSnapshot {
        /// Rejected handle shape.
        handle: PluginHandleShape,
    },
    /// The captured text exceeds the configured return-message bound.
    TextTooLarge {
        /// Rejected handle shape.
        handle: PluginHandleShape,
        /// Captured text byte length.
        byte_len: u64,
        /// Maximum return bytes.
        max: u64,
    },
}

impl Display for PluginBufferObservationError {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::MissingSnapshot { handle } => write!(
                formatter,
                "plugin buffer observation for {} handle lacks captured text",
                handle.kind()
            ),
            Self::TextTooLarge { byte_len, max, .. } => write!(
                formatter,
                "plugin buffer observation text has {byte_len} bytes, exceeding {max}"
            ),
        }
    }
}

/// Host-import rejection.
#[derive(Clone, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum PluginHostImportError {
    /// Host import state came from different plugin identities.
    HostStateMismatch(#[source] PluginHostStateMismatch),
    /// Capability was denied.
    Authorization(#[source] PluginAuthorizationError),
    /// Handle resolution failed.
    Handle(#[source] PluginHandleError),
    /// Editor effect queuing failed.
    Effect(#[source] PluginEffectCommitError),
    /// Workspace I/O queuing failed.
    WorkspaceIo(#[source] PluginWorkspaceIoCommitError),
    /// Workspace observation task reservation failed.
    WorkspaceObserveTask(#[source] PluginWorkspaceObserveTaskReserveError),
    /// Buffer observation failed after authorization.
    Observation(#[source] PluginBufferObservationError),
    /// Guest data failed decode before authorization.
    Decode(#[source] GuestDecodeError),
    /// Component-model resource handle could not be resolved.
    ResourceHandle {
        /// Expected resource kind.
        kind: PluginHostResourceKind,
        /// Rejection shape.
        reason: PluginResourceHandleRejectionReason,
    },
    /// No active update import session exists.
    SessionMissing,
    /// An update import session is already active.
    SessionAlreadyActive,
    /// Buffer edit lacked revision provenance.
    MissingObservedRevision {
        /// Rejected handle shape.
        handle: PluginHandleShape,
    },
}

impl PluginHostImportError {
    /// Stable debug class for the rejected import boundary.
    #[must_use]
    const fn kind(&self) -> PluginHostImportErrorKind {
        match self {
            Self::HostStateMismatch(_) => PluginHostImportErrorKind::HostStateMismatch,
            Self::Authorization(_) => PluginHostImportErrorKind::Authorization,
            Self::Handle(_) => PluginHostImportErrorKind::Handle,
            Self::Effect(_) => PluginHostImportErrorKind::Effect,
            Self::WorkspaceIo(_) => PluginHostImportErrorKind::WorkspaceIo,
            Self::WorkspaceObserveTask(_) => PluginHostImportErrorKind::WorkspaceObserveTask,
            Self::Observation(_) => PluginHostImportErrorKind::Observation,
            Self::Decode(_) => PluginHostImportErrorKind::Decode,
            Self::ResourceHandle { .. } => PluginHostImportErrorKind::ResourceHandle,
            Self::SessionMissing => PluginHostImportErrorKind::SessionMissing,
            Self::SessionAlreadyActive => PluginHostImportErrorKind::SessionAlreadyActive,
            Self::MissingObservedRevision { .. } => {
                PluginHostImportErrorKind::MissingObservedRevision
            }
        }
    }

    /// Redacted rejection class for non-authorization host-import failures.
    ///
    /// Authorization failures become denied-import events rather than rejected-import events.
    #[must_use]
    pub const fn operational_rejection(&self) -> Option<PluginOperationalImportRejection> {
        match self {
            Self::HostStateMismatch(_) => Some(PluginOperationalImportRejection::HostStateMismatch),
            Self::Authorization(_) => None,
            Self::Handle(_) => Some(PluginOperationalImportRejection::Handle),
            Self::Effect(_) => Some(PluginOperationalImportRejection::EffectQueue),
            Self::WorkspaceIo(_) => Some(PluginOperationalImportRejection::WorkspaceIoQueue),
            Self::WorkspaceObserveTask(source) => Some(source.operational_rejection()),
            Self::Observation(_) => Some(PluginOperationalImportRejection::Observation),
            Self::Decode(_) => Some(PluginOperationalImportRejection::Decode),
            Self::ResourceHandle { .. } => Some(PluginOperationalImportRejection::ResourceHandle),
            Self::SessionMissing => Some(PluginOperationalImportRejection::SessionMissing),
            Self::SessionAlreadyActive => {
                Some(PluginOperationalImportRejection::SessionAlreadyActive)
            }
            Self::MissingObservedRevision { .. } => {
                Some(PluginOperationalImportRejection::MissingObservedRevision)
            }
        }
    }
}

impl Debug for PluginHostImportError {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        let mut debug = formatter.debug_struct("PluginHostImportError");
        let _debug = debug.field("kind", &self.kind());
        let _debug = debug.field("operational_rejection", &self.operational_rejection());
        match self {
            Self::HostStateMismatch(source) => {
                let _debug = debug.field("source", source);
            }
            Self::Authorization(source) => {
                let _debug = debug.field("source", source);
            }
            Self::Handle(source) => {
                let _debug = debug.field("source", source);
            }
            Self::Effect(source) => {
                let _debug = debug.field("source", source);
            }
            Self::WorkspaceIo(source) => {
                let _debug = debug.field("source", source);
            }
            Self::WorkspaceObserveTask(source) => {
                let _debug = debug.field("source", source);
            }
            Self::Observation(source) => {
                let _debug = debug.field("source", source);
            }
            Self::Decode(source) => {
                let _debug = debug.field("source", source);
            }
            Self::ResourceHandle { kind, reason } => {
                let _debug = debug.field("resource_kind", kind);
                let _debug = debug.field("reason", reason);
            }
            Self::SessionMissing | Self::SessionAlreadyActive => {}
            Self::MissingObservedRevision { handle } => {
                let _debug = debug.field("handle", handle);
            }
        }
        debug.finish()
    }
}

/// Closed host-import error kind used by redacted debug output.
#[derive(Clone, Copy, Eq, PartialEq)]
pub(super) enum PluginHostImportErrorKind {
    /// Host import state came from different plugin identities.
    HostStateMismatch,
    /// Capability was denied.
    Authorization,
    /// Handle resolution failed.
    Handle,
    /// Editor effect queuing failed.
    Effect,
    /// Workspace I/O queuing failed.
    WorkspaceIo,
    /// Workspace observation task reservation failed.
    WorkspaceObserveTask,
    /// Buffer observation failed after authorization.
    Observation,
    /// Guest data failed decode before authorization.
    Decode,
    /// Component-model resource handle could not be resolved.
    ResourceHandle,
    /// No active update import session exists.
    SessionMissing,
    /// An update import session is already active.
    SessionAlreadyActive,
    /// Buffer edit lacked revision provenance.
    MissingObservedRevision,
}

impl PluginHostImportErrorKind {
    /// Stable debug class text.
    pub(super) const fn as_str(self) -> &'static str {
        match self {
            Self::HostStateMismatch => "host-state-mismatch",
            Self::Authorization => "authorization",
            Self::Handle => "handle",
            Self::Effect => "effect",
            Self::WorkspaceIo => "workspace-io",
            Self::WorkspaceObserveTask => "workspace-observe-task",
            Self::Observation => "observation",
            Self::Decode => "decode",
            Self::ResourceHandle => "resource-handle",
            Self::SessionMissing => "session-missing",
            Self::SessionAlreadyActive => "session-already-active",
            Self::MissingObservedRevision => "missing-observed-revision",
        }
    }
}

impl Debug for PluginHostImportErrorKind {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        formatter.write_str(self.as_str())
    }
}

impl Display for PluginHostImportErrorKind {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        formatter.write_str(self.as_str())
    }
}

impl Display for PluginHostImportError {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::HostStateMismatch(source) => {
                write!(formatter, "plugin import host state denied: {source}")
            }
            Self::Authorization(source) => write!(formatter, "plugin import denied: {source}"),
            Self::Handle(source) => write!(formatter, "plugin import handle denied: {source}"),
            Self::Effect(source) => write!(formatter, "plugin import effect denied: {source}"),
            Self::WorkspaceIo(source) => {
                write!(formatter, "plugin import workspace I/O denied: {source}")
            }
            Self::WorkspaceObserveTask(source) => {
                write!(
                    formatter,
                    "plugin import workspace observation task denied: {source}"
                )
            }
            Self::Observation(source) => {
                write!(formatter, "plugin import observation denied: {source}")
            }
            Self::Decode(source) => write!(formatter, "plugin import decode denied: {source}"),
            Self::ResourceHandle { kind, reason } => {
                write!(formatter, "plugin import resource {kind} denied: {reason}")
            }
            Self::SessionMissing => {
                formatter.write_str("plugin import attempted outside active update")
            }
            Self::SessionAlreadyActive => {
                formatter.write_str("plugin import attempted to start a nested update")
            }
            Self::MissingObservedRevision { handle } => write!(
                formatter,
                "plugin buffer edit for {} handle lacks observed revision",
                handle.kind()
            ),
        }
    }
}

/// Host context and handle store identity mismatch.
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[error(
    "host identity {:?} does not match handle store identity {:?}",
    .host_identity.as_str(),
    .handle_identity.as_str()
)]
pub struct PluginHostStateMismatch {
    /// Identity that owns the captured grants.
    host_identity: PluginIdentity,
    /// Identity that owns the captured handles.
    handle_identity: PluginIdentity,
}

impl PluginHostStateMismatch {
    /// Records a rejected host-state pairing.
    #[must_use]
    pub(in crate::plugin) const fn new(
        host_identity: PluginIdentity,
        handle_identity: PluginIdentity,
    ) -> Self {
        Self {
            host_identity,
            handle_identity,
        }
    }

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

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

impl From<PluginHostStateMismatch> for PluginHostImportError {
    fn from(source: PluginHostStateMismatch) -> Self {
        Self::HostStateMismatch(source)
    }
}

impl From<PluginAuthorizationError> for PluginHostImportError {
    fn from(source: PluginAuthorizationError) -> Self {
        Self::Authorization(source)
    }
}

impl From<PluginHandleError> for PluginHostImportError {
    fn from(source: PluginHandleError) -> Self {
        Self::Handle(source)
    }
}

impl From<PluginEffectCommitError> for PluginHostImportError {
    fn from(source: PluginEffectCommitError) -> Self {
        Self::Effect(source)
    }
}

impl From<PluginWorkspaceIoCommitError> for PluginHostImportError {
    fn from(source: PluginWorkspaceIoCommitError) -> Self {
        Self::WorkspaceIo(source)
    }
}

impl From<PluginWorkspaceObserveTaskReserveError> for PluginHostImportError {
    fn from(source: PluginWorkspaceObserveTaskReserveError) -> Self {
        Self::WorkspaceObserveTask(source)
    }
}

impl From<PluginBufferObservationError> for PluginHostImportError {
    fn from(source: PluginBufferObservationError) -> Self {
        Self::Observation(source)
    }
}

impl From<GuestDecodeError> for PluginHostImportError {
    fn from(source: GuestDecodeError) -> Self {
        Self::Decode(source)
    }
}