alma 0.1.1

A Bevy-native modal text editor with Vim-style navigation.
Documentation
//! Buffer edit decoders.

use super::{
    DecodedCount, DecodedGuestString, DecodedTextRange, GuestDecodeError, GuestDecodeField,
    GuestDecodeLimits,
};
use crate::buffer::BufferEdit;

/// Guest buffer edit after scalar, range, text, and payload-limit checks.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DecodedBufferEdit {
    /// Replace all buffer text.
    SetAll {
        /// Bounded replacement text.
        text: DecodedGuestString,
    },
    /// Insert text at a byte boundary.
    Insert {
        /// Requested byte index.
        byte_index: DecodedCount,
        /// Bounded inserted text.
        text: DecodedGuestString,
    },
    /// Replace an unchecked byte range.
    Replace {
        /// Requested byte range.
        range: DecodedTextRange,
        /// Bounded replacement text.
        text: DecodedGuestString,
    },
    /// Delete an unchecked byte range.
    Delete {
        /// Requested byte range.
        range: DecodedTextRange,
    },
}

impl DecodedBufferEdit {
    /// Decodes a whole-buffer replacement.
    ///
    /// # Errors
    ///
    /// Returns [`GuestDecodeError`] when the replacement text is oversized.
    pub fn set_all(text: &str, limits: GuestDecodeLimits) -> Result<Self, GuestDecodeError> {
        Ok(Self::SetAll {
            text: DecodedGuestString::from_bytes(
                GuestDecodeField::BufferEditText,
                text.as_bytes(),
                limits,
            )?,
        })
    }

    /// Decodes an insertion edit.
    ///
    /// # Errors
    ///
    /// Returns [`GuestDecodeError`] when the byte index or text exceeds runtime limits.
    pub fn insert(
        byte_index: u64,
        text: &str,
        limits: GuestDecodeLimits,
    ) -> Result<Self, GuestDecodeError> {
        Ok(Self::Insert {
            byte_index: DecodedCount::new(
                GuestDecodeField::BufferEditByteIndex,
                byte_index,
                limits,
            )?,
            text: DecodedGuestString::from_bytes(
                GuestDecodeField::BufferEditText,
                text.as_bytes(),
                limits,
            )?,
        })
    }

    /// Decodes a replacement edit.
    ///
    /// # Errors
    ///
    /// Returns [`GuestDecodeError`] when the range or text exceeds runtime limits.
    pub fn replace(
        start: u64,
        end: u64,
        text: &str,
        limits: GuestDecodeLimits,
    ) -> Result<Self, GuestDecodeError> {
        Ok(Self::Replace {
            range: DecodedTextRange::new(GuestDecodeField::BufferEditRange, start, end, limits)?,
            text: DecodedGuestString::from_bytes(
                GuestDecodeField::BufferEditText,
                text.as_bytes(),
                limits,
            )?,
        })
    }

    /// Decodes a deletion edit.
    ///
    /// # Errors
    ///
    /// Returns [`GuestDecodeError`] when the range exceeds runtime limits.
    pub fn delete(
        start: u64,
        end: u64,
        limits: GuestDecodeLimits,
    ) -> Result<Self, GuestDecodeError> {
        Ok(Self::Delete {
            range: DecodedTextRange::new(GuestDecodeField::BufferEditRange, start, end, limits)?,
        })
    }

    /// Converts the decoded request into an owner-validated buffer edit.
    #[must_use]
    pub fn into_buffer_edit(self) -> BufferEdit {
        match self {
            Self::SetAll { text } => BufferEdit::set_all(text.into_string()),
            Self::Insert { byte_index, text } => {
                BufferEdit::insert(byte_index.get(), text.into_string())
            }
            Self::Replace { range, text } => {
                BufferEdit::replace_unchecked(range.get(), text.into_string())
            }
            Self::Delete { range } => BufferEdit::delete_unchecked(range.get()),
        }
    }
}