alma 0.1.1

A Bevy-native modal text editor with Vim-style navigation.
Documentation
//! Effect payload and proposal vocabulary.

use crate::{
    buffer::BufferEditShape,
    ecs::{
        components::buffer::BufferEntity,
        events::{
            edit::{PluginBufferEditProvenance, RevisionGuardedBufferEditRequested},
            status::{StatusMessage, StatusMessageRequested},
        },
    },
    plugin::PluginIdentity,
    text_stream::TextRevision,
};
use std::fmt::{Debug, Formatter};

/// Host-accepted proposals not yet published to ECS.
#[derive(Eq, PartialEq)]
pub(in crate::plugin::host::effect) enum PluginEffect {
    /// Buffer edit proposal.
    BufferEdit(PluginBufferEditProposal),
    /// Status-line request.
    Status(StatusMessageRequested),
}

impl PluginEffect {
    /// Returns its diagnostic shape.
    pub(in crate::plugin::host::effect) fn shape(&self) -> PluginEffectShape {
        PluginEffectShape::from(self)
    }
}

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

/// Plugin buffer edit proposal with observed revision provenance.
#[derive(Eq, PartialEq)]
pub struct PluginBufferEditProposal {
    /// Target buffer entity.
    target: BufferEntity,
    /// Owner-boundary edit payload.
    edit: crate::buffer::BufferEdit,
    /// Revision observed by the guest.
    base_revision: TextRevision,
}

impl PluginBufferEditProposal {
    /// Creates a snapshot-derived plugin proposal.
    #[must_use]
    pub const fn from_observed_revision(
        target: BufferEntity,
        edit: crate::buffer::BufferEdit,
        base_revision: TextRevision,
    ) -> Self {
        Self {
            target,
            edit,
            base_revision,
        }
    }

    /// Returns the target buffer.
    #[must_use]
    pub const fn target(&self) -> BufferEntity {
        self.target
    }

    /// Returns the observed revision.
    #[must_use]
    pub const fn base_revision(&self) -> TextRevision {
        self.base_revision
    }

    /// Converts into an owner-boundary request.
    pub(in crate::plugin::host::effect) fn into_request(
        self,
        source_identity: &PluginIdentity,
    ) -> RevisionGuardedBufferEditRequested {
        RevisionGuardedBufferEditRequested {
            target: self.target,
            provenance: PluginBufferEditProvenance::buffer_propose_edit(
                source_identity.clone(),
                self.base_revision,
            ),
            edit: self.edit,
        }
    }
}

impl Debug for PluginBufferEditProposal {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        formatter
            .debug_struct("PluginBufferEditProposal")
            .field("target", &PluginEffectTargetShape::Buffer)
            .field("edit", &BufferEditShape::from_edit(&self.edit))
            .field("base_revision", &self.base_revision)
            .finish()
    }
}

/// Redacted owner target kind.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::plugin::host::effect) enum PluginEffectTargetShape {
    /// Buffer owner target.
    Buffer,
    /// View owner target.
    View,
}

/// Redacted effect shape.
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::plugin::host::effect) enum PluginEffectShape {
    /// Buffer edit proposal effect.
    BufferEdit {
        /// Redacted edit shape.
        edit: BufferEditShape,
    },
    /// Status effect.
    Status {
        /// Redacted status shape.
        status: PluginStatusMessageShape,
    },
}

impl From<&PluginEffect> for PluginEffectShape {
    fn from(effect: &PluginEffect) -> Self {
        match effect {
            PluginEffect::BufferEdit(effect) => Self::BufferEdit {
                edit: BufferEditShape::from_edit(&effect.edit),
            },
            PluginEffect::Status(request) => Self::Status {
                status: PluginStatusMessageShape::from(&request.message),
            },
        }
    }
}

/// Redacted status shape.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::plugin::host::effect) enum PluginStatusMessageShape {
    /// Clear status.
    Clear,
    /// Informational status length.
    Info {
        /// Escaped text byte length.
        byte_len: usize,
    },
    /// Error status.
    Error,
}

impl From<&StatusMessage> for PluginStatusMessageShape {
    fn from(message: &StatusMessage) -> Self {
        match message {
            StatusMessage::Clear => Self::Clear,
            StatusMessage::Info(text) => Self::Info {
                byte_len: text.as_str().len(),
            },
            StatusMessage::Error(_error) => Self::Error,
        }
    }
}