alma 0.1.1

A Bevy-native modal text editor with Vim-style navigation.
Documentation
//! Workspace I/O commit error vocabulary.

use super::PluginWorkspaceIoBudgetField;
use crate::plugin::PluginIdentity;
use std::{
    fmt::{Debug, Display, Formatter},
    num::NonZeroUsize,
};

/// Workspace I/O identity pairing source.
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PluginWorkspaceIoIdentityMismatchSource {
    /// A workspace access proof came from another plugin.
    AccessProof,
    /// A paired request ledger came from another plugin.
    RequestLedger,
}

impl PluginWorkspaceIoIdentityMismatchSource {
    /// Stable diagnostic spelling.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::AccessProof => "access-proof",
            Self::RequestLedger => "request-ledger",
        }
    }
}

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

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

/// Workspace I/O commit rejection class.
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PluginWorkspaceIoCommitErrorKind {
    /// A configured budget limit was zero.
    ZeroLimit,
    /// Write payload exceeded the accepted byte cap.
    PayloadTooLarge,
    /// The per-update request cap was exhausted.
    TooManyRequests,
    /// A workspace proof or paired request ledger belonged to another plugin.
    IdentityMismatch,
}

impl PluginWorkspaceIoCommitErrorKind {
    /// Stable diagnostic spelling.
    #[must_use]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::ZeroLimit => "zero-limit",
            Self::PayloadTooLarge => "payload-too-large",
            Self::TooManyRequests => "too-many-requests",
            Self::IdentityMismatch => "identity-mismatch",
        }
    }
}

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

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

/// Errors returned while accepting pending workspace I/O from one plugin update.
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum PluginWorkspaceIoCommitError {
    /// A workspace I/O budget limit was zero.
    ZeroLimit {
        /// Budget field name.
        field: PluginWorkspaceIoBudgetField,
    },
    /// Write payload exceeds the workspace I/O budget.
    PayloadTooLarge {
        /// Observed payload bytes.
        size: u64,
        /// Accepted payload bytes.
        max_size: u64,
    },
    /// Guest update attempted to enqueue too many workspace I/O requests.
    TooManyRequests {
        /// Stable plugin identity.
        identity: PluginIdentity,
        /// Maximum accepted requests.
        limit: NonZeroUsize,
    },
    /// A workspace access proof or paired request ledger came from another plugin identity.
    IdentityMismatch {
        /// Boundary that provided the mismatched identity.
        mismatch_source: PluginWorkspaceIoIdentityMismatchSource,
        /// Identity that owns the pending batch.
        batch_identity: PluginIdentity,
        /// Identity that authorized access or owns the paired ledger.
        provided_identity: PluginIdentity,
    },
}

impl PluginWorkspaceIoCommitError {
    /// Closed rejection class for caller decisions and diagnostics.
    #[must_use]
    pub const fn kind(&self) -> PluginWorkspaceIoCommitErrorKind {
        match self {
            Self::ZeroLimit { .. } => PluginWorkspaceIoCommitErrorKind::ZeroLimit,
            Self::PayloadTooLarge { .. } => PluginWorkspaceIoCommitErrorKind::PayloadTooLarge,
            Self::TooManyRequests { .. } => PluginWorkspaceIoCommitErrorKind::TooManyRequests,
            Self::IdentityMismatch { .. } => PluginWorkspaceIoCommitErrorKind::IdentityMismatch,
        }
    }
}

impl Display for PluginWorkspaceIoCommitError {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ZeroLimit { field } => {
                write!(
                    formatter,
                    "plugin workspace I/O budget {field} must be non-zero"
                )
            }
            Self::PayloadTooLarge { size, max_size } => write!(
                formatter,
                "plugin workspace payload is {size} bytes, exceeding the {max_size} byte limit"
            ),
            Self::TooManyRequests { identity, limit } => {
                let identity = identity.as_str();
                let limit = limit.get();
                write!(
                    formatter,
                    "plugin {identity:?} exceeded workspace I/O request limit of {limit}"
                )
            }
            Self::IdentityMismatch {
                mismatch_source,
                batch_identity,
                provided_identity,
            } => {
                let batch_identity = batch_identity.as_str();
                let provided_identity = provided_identity.as_str();
                match mismatch_source {
                    PluginWorkspaceIoIdentityMismatchSource::AccessProof => write!(
                        formatter,
                        "plugin workspace I/O access proof for {provided_identity:?} cannot be queued by {batch_identity:?}"
                    ),
                    PluginWorkspaceIoIdentityMismatchSource::RequestLedger => write!(
                        formatter,
                        "plugin workspace I/O request ledger for {provided_identity:?} cannot be paired with task batch {batch_identity:?}"
                    ),
                }
            }
        }
    }
}