alma 0.1.1

A Bevy-native modal text editor with Vim-style navigation.
Documentation
use crate::{
    fs_utils::{EscapedDisplayText, FilesystemConfig, read_existing_workspace_file},
    plugin::{PluginComponentOpenError, PluginComponentPath, PluginIdentity},
};
use std::fmt::{Debug, Formatter};

/// Component bytes opened through filesystem policy.
#[derive(Eq, PartialEq)]
pub struct AuthorizedPluginComponent {
    /// Plugin identity tied to these bytes.
    identity: PluginIdentity,
    /// Escaped path for diagnostics.
    display_path: EscapedDisplayText,
    /// Policy-opened component bytes.
    bytes: Vec<u8>,
}

impl AuthorizedPluginComponent {
    /// Opens component bytes for one approved plugin.
    ///
    /// # Errors
    ///
    /// Returns [`PluginComponentOpenError`] when filesystem policy rejects the read.
    pub fn open(
        identity: PluginIdentity,
        component_path: &PluginComponentPath,
        filesystem: &FilesystemConfig,
        max_bytes: u64,
    ) -> Result<Self, PluginComponentOpenError> {
        let file = read_existing_workspace_file(component_path.as_path(), filesystem, max_bytes)
            .map_err(|source| PluginComponentOpenError::Read {
                identity: identity.clone(),
                source,
            })?;
        Ok(Self {
            identity,
            display_path: EscapedDisplayText::from_path(&file.path),
            bytes: file.bytes,
        })
    }

    /// Returns the plugin identity tied to these bytes.
    #[must_use]
    pub const fn identity(&self) -> &PluginIdentity {
        &self.identity
    }

    /// Returns the escaped component path for diagnostics.
    #[must_use]
    pub const fn display_path(&self) -> &EscapedDisplayText {
        &self.display_path
    }

    /// Returns policy-opened component bytes.
    #[must_use]
    pub fn bytes(&self) -> &[u8] {
        &self.bytes
    }

    #[cfg(test)]
    /// Builds component bytes without filesystem access.
    pub(crate) const fn for_test(
        identity: PluginIdentity,
        display_path: EscapedDisplayText,
        bytes: Vec<u8>,
    ) -> Self {
        Self {
            identity,
            display_path,
            bytes,
        }
    }
}

impl Debug for AuthorizedPluginComponent {
    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
        formatter
            .debug_struct("AuthorizedPluginComponent")
            .field("identity", &self.identity)
            .field("display_path_byte_len", &self.display_path.as_str().len())
            .field("byte_len", &self.bytes.len())
            .finish()
    }
}