alma 0.1.1

A Bevy-native modal text editor with Vim-style navigation.
Documentation
use crate::{
    buffer::{BufferEditKind, BufferEditShape},
    plugin::{PluginOperationalEventKind, PluginRevocationReason},
    text_stream::TextRange,
};
use std::fmt::{Display, Formatter};

/// Display adapter for redacted edit shapes.
pub(super) struct EditShape<'shape>(pub(super) &'shape BufferEditShape);

impl Display for EditShape<'_> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        write!(formatter, "{}", buffer_edit_kind(self.0.kind()))?;
        if let Some(byte_index) = self.0.byte_index() {
            write!(formatter, " byte_index={byte_index}")?;
        }
        if let Some(range) = self.0.range() {
            write!(formatter, " range={}", TraceRange(range))?;
        }
        if let Some(replacement_byte_len) = self.0.replacement_byte_len() {
            write!(formatter, " replacement_byte_len={replacement_byte_len}")?;
        }
        Ok(())
    }
}

/// Display adapter for redacted byte ranges.
struct TraceRange(TextRange);

impl Display for TraceRange {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        write!(formatter, "{}..{}", self.0.start(), self.0.end())
    }
}

/// Display adapter for closed operational event shapes.
pub(super) struct Operational<'event>(pub(super) &'event PluginOperationalEventKind);

impl Display for Operational<'_> {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        match self.0 {
            PluginOperationalEventKind::Load => formatter.write_str("load"),
            PluginOperationalEventKind::Unload => formatter.write_str("unload"),
            PluginOperationalEventKind::Revocation { reason } => {
                write!(formatter, "revocation reason={}", RevocationReason(*reason))
            }
            PluginOperationalEventKind::DeniedImport { import } => {
                write!(formatter, "denied-import import={import}")
            }
            PluginOperationalEventKind::ImportRejected { import, reason } => {
                write!(formatter, "import-rejected import={import} reason={reason}")
            }
            PluginOperationalEventKind::Timeout { timeout_ms } => {
                write!(formatter, "timeout timeout_ms={timeout_ms}")
            }
            PluginOperationalEventKind::GuestTrap { export } => {
                write!(formatter, "guest-trap export={export}")
            }
            PluginOperationalEventKind::RuntimeFailure { failure } => {
                write!(formatter, "runtime-failure failure={failure}")
            }
            PluginOperationalEventKind::QueueSaturated { queue, limit } => {
                write!(formatter, "queue-saturated queue={queue} limit={limit}")
            }
        }
    }
}

/// Display adapter for revocation reasons.
struct RevocationReason(PluginRevocationReason);

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

/// Returns stable trace text for a buffer edit kind.
const fn buffer_edit_kind(kind: BufferEditKind) -> &'static str {
    match kind {
        BufferEditKind::SetAll => "set-all",
        BufferEditKind::Insert => "insert",
        BufferEditKind::Replace => "replace",
        BufferEditKind::Delete => "delete",
    }
}

/// Writes a semicolon-separated trace list.
pub(super) fn write_list<T: Display>(
    formatter: &mut Formatter<'_>,
    values: &[T],
) -> std::fmt::Result {
    for (index, value) in values.iter().enumerate() {
        if index > 0 {
            formatter.write_str("; ")?;
        }
        write!(formatter, "{value}")?;
    }
    Ok(())
}