alma 0.1.1

A Bevy-native modal text editor with Vim-style navigation.
Documentation
//! Effect discard and drain reports.

use super::proposal::{PluginEffectTargetShape, PluginStatusMessageShape};
use crate::{
    buffer::BufferEditShape,
    ecs::events::{
        edit::{PluginBufferProposalCapability, RevisionGuardedBufferEditRequested},
        status::StatusMessageRequested,
    },
    plugin::PluginIdentity,
    text_stream::TextRevision,
};
use std::fmt::{Debug, Formatter};

/// Effects discarded before successful return.
#[derive(Debug, Eq, PartialEq)]
pub struct PluginEffectDiscardReport {
    /// Stable plugin identity.
    identity: PluginIdentity,
    /// Effects dropped before commit.
    discarded_effects: usize,
}

impl PluginEffectDiscardReport {
    /// Creates discard evidence for uncommitted or unpublished effects.
    #[must_use]
    pub(in crate::plugin::host::effect) const fn new(
        identity: PluginIdentity,
        discarded_effects: usize,
    ) -> Self {
        Self {
            identity,
            discarded_effects,
        }
    }

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

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

    /// Returns the discarded effect count.
    #[must_use]
    pub const fn discarded_effects(&self) -> usize {
        self.discarded_effects
    }
}

/// Drained ECS request messages.
#[derive(Eq, PartialEq)]
pub struct DrainedPluginEffectReport {
    /// Stable plugin identity.
    identity: PluginIdentity,
    /// Revision-guarded edit requests.
    buffer_edits: Vec<RevisionGuardedBufferEditRequested>,
    /// Status owner requests.
    status_messages: Vec<StatusMessageRequested>,
}

impl DrainedPluginEffectReport {
    /// Creates drained-effect evidence after owner request projection.
    #[must_use]
    pub(in crate::plugin::host::effect) const fn from_requests(
        identity: PluginIdentity,
        buffer_edits: Vec<RevisionGuardedBufferEditRequested>,
        status_messages: Vec<StatusMessageRequested>,
    ) -> Self {
        Self {
            identity,
            buffer_edits,
            status_messages,
        }
    }

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

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

    /// Returns buffer edit requests.
    #[must_use]
    pub fn buffer_edits(&self) -> &[RevisionGuardedBufferEditRequested] {
        &self.buffer_edits
    }

    /// Returns status message requests.
    #[must_use]
    pub fn status_messages(&self) -> &[StatusMessageRequested] {
        &self.status_messages
    }

    /// Consumes the report into named owner request batches.
    #[must_use]
    pub fn into_requests(self) -> DrainedPluginEffectRequests {
        DrainedPluginEffectRequests {
            identity: self.identity,
            buffer_edits: self.buffer_edits,
            status_messages: self.status_messages,
        }
    }
}

impl Debug for DrainedPluginEffectReport {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        formatter
            .debug_struct("DrainedPluginEffectReport")
            .field("identity", &self.identity)
            .field("buffer_edit_count", &self.buffer_edits.len())
            .field(
                "buffer_edit_shapes",
                &self
                    .buffer_edits
                    .iter()
                    .map(DrainedPluginBufferEditShape::from)
                    .collect::<Vec<_>>(),
            )
            .field("status_message_count", &self.status_messages.len())
            .field(
                "status_message_shapes",
                &self
                    .status_messages
                    .iter()
                    .map(DrainedPluginStatusMessageShape::from)
                    .collect::<Vec<_>>(),
            )
            .finish()
    }
}

/// Owner request batches produced by draining plugin effects.
#[derive(Eq, PartialEq)]
pub struct DrainedPluginEffectRequests {
    /// Stable plugin identity for the drained effects.
    pub identity: PluginIdentity,
    /// Buffer proposal owner requests.
    pub buffer_edits: Vec<RevisionGuardedBufferEditRequested>,
    /// Status owner requests.
    pub status_messages: Vec<StatusMessageRequested>,
}

impl DrainedPluginEffectRequests {
    /// Returns the plugin identity for display and serialization.
    #[must_use]
    pub fn identity(&self) -> &str {
        self.identity_proof().as_str()
    }

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

/// Redacted drained buffer edit request shape.
#[derive(Clone, Debug, Eq, PartialEq)]
struct DrainedPluginBufferEditShape {
    /// Buffer owner target kind.
    target: PluginEffectTargetShape,
    /// Stable plugin identity that proposed the edit.
    source_identity: PluginIdentity,
    /// Capability used to propose the edit.
    capability: PluginBufferProposalCapability,
    /// Revision observed before the proposal was built.
    base_revision: TextRevision,
    /// Redacted edit shape.
    edit: BufferEditShape,
}

impl From<&RevisionGuardedBufferEditRequested> for DrainedPluginBufferEditShape {
    fn from(request: &RevisionGuardedBufferEditRequested) -> Self {
        Self {
            target: PluginEffectTargetShape::Buffer,
            source_identity: request.provenance.source_identity_proof().clone(),
            capability: request.provenance.capability(),
            base_revision: request.provenance.base_revision(),
            edit: BufferEditShape::from_edit(&request.edit),
        }
    }
}

/// Redacted drained status request shape.
#[derive(Clone, Debug, Eq, PartialEq)]
struct DrainedPluginStatusMessageShape {
    /// View owner target kind.
    target: PluginEffectTargetShape,
    /// Redacted status payload shape.
    status: PluginStatusMessageShape,
}

impl From<&StatusMessageRequested> for DrainedPluginStatusMessageShape {
    fn from(request: &StatusMessageRequested) -> Self {
        Self {
            target: PluginEffectTargetShape::View,
            status: PluginStatusMessageShape::from(&request.message),
        }
    }
}