lcpfs 2026.1.102

LCP File System - A ZFS-inspired copy-on-write filesystem for Rust
// Copyright 2025 LunaOS Contributors
// SPDX-License-Identifier: Apache-2.0

//! WASM plugin error types.
//!
//! This module defines errors that can occur during plugin loading,
//! execution, and management.

use alloc::string::String;

use super::types::ManifestError;

// ═══════════════════════════════════════════════════════════════════════════════
// PLUGIN ERROR
// ═══════════════════════════════════════════════════════════════════════════════

/// Errors that can occur with WASM plugins.
#[derive(Debug)]
pub enum PluginError {
    /// WASM module failed to compile
    CompilationFailed(String),
    /// WASM module failed to instantiate
    InstantiationFailed(String),
    /// Plugin initialization failed (plugin_init returned non-zero)
    InitFailed(i32),
    /// Plugin manifest is invalid
    InvalidManifest(ManifestError),
    /// Plugin not found
    NotFound(String),
    /// Plugin already loaded
    AlreadyLoaded(String),
    /// Plugin execution failed
    ExecutionFailed(String),
    /// Plugin exceeded resource limits
    ResourceLimitExceeded(String),
    /// Permission denied
    PermissionDenied(String),
    /// Memory allocation failed in WASM
    MemoryError(String),
    /// Required function not exported
    MissingExport(String),
    /// Invalid function signature
    InvalidSignature(String),
    /// Plugin returned an error code
    PluginReturnedError(i32),
    /// I/O error during plugin operation
    IoError(String),
    /// Hook not implemented by plugin
    HookNotImplemented,
    /// Internal error
    Internal(String),
}

impl PluginError {
    /// Get a human-readable error message.
    pub fn message(&self) -> String {
        match self {
            PluginError::CompilationFailed(msg) => {
                alloc::format!("WASM compilation failed: {}", msg)
            }
            PluginError::InstantiationFailed(msg) => {
                alloc::format!("WASM instantiation failed: {}", msg)
            }
            PluginError::InitFailed(code) => {
                alloc::format!("Plugin initialization failed with code {}", code)
            }
            PluginError::InvalidManifest(e) => {
                alloc::format!("Invalid plugin manifest: {:?}", e)
            }
            PluginError::NotFound(name) => {
                alloc::format!("Plugin not found: {}", name)
            }
            PluginError::AlreadyLoaded(name) => {
                alloc::format!("Plugin already loaded: {}", name)
            }
            PluginError::ExecutionFailed(msg) => {
                alloc::format!("Plugin execution failed: {}", msg)
            }
            PluginError::ResourceLimitExceeded(resource) => {
                alloc::format!("Resource limit exceeded: {}", resource)
            }
            PluginError::PermissionDenied(perm) => {
                alloc::format!("Permission denied: {}", perm)
            }
            PluginError::MemoryError(msg) => {
                alloc::format!("WASM memory error: {}", msg)
            }
            PluginError::MissingExport(name) => {
                alloc::format!("Missing required export: {}", name)
            }
            PluginError::InvalidSignature(name) => {
                alloc::format!("Invalid function signature: {}", name)
            }
            PluginError::PluginReturnedError(code) => {
                alloc::format!("Plugin returned error code {}", code)
            }
            PluginError::IoError(msg) => {
                alloc::format!("I/O error: {}", msg)
            }
            PluginError::HookNotImplemented => "Hook not implemented".into(),
            PluginError::Internal(msg) => {
                alloc::format!("Internal error: {}", msg)
            }
        }
    }

    /// Check if this is a recoverable error.
    pub fn is_recoverable(&self) -> bool {
        matches!(
            self,
            PluginError::ResourceLimitExceeded(_)
                | PluginError::PluginReturnedError(_)
                | PluginError::HookNotImplemented
        )
    }
}

impl From<ManifestError> for PluginError {
    fn from(e: ManifestError) -> Self {
        PluginError::InvalidManifest(e)
    }
}

// ═══════════════════════════════════════════════════════════════════════════════
// TESTS
// ═══════════════════════════════════════════════════════════════════════════════

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_messages() {
        let err = PluginError::NotFound("test-plugin".into());
        assert!(err.message().contains("test-plugin"));

        let err = PluginError::InitFailed(-1);
        assert!(err.message().contains("-1"));

        let err = PluginError::ResourceLimitExceeded("memory".into());
        assert!(err.message().contains("memory"));
    }

    #[test]
    fn test_is_recoverable() {
        assert!(PluginError::ResourceLimitExceeded("memory".into()).is_recoverable());
        assert!(PluginError::PluginReturnedError(1).is_recoverable());
        assert!(PluginError::HookNotImplemented.is_recoverable());

        assert!(!PluginError::CompilationFailed("bad wasm".into()).is_recoverable());
        assert!(!PluginError::InitFailed(-1).is_recoverable());
    }

    #[test]
    fn test_from_manifest_error() {
        let manifest_err = ManifestError::MissingField("name");
        let plugin_err: PluginError = manifest_err.into();
        assert!(matches!(plugin_err, PluginError::InvalidManifest(_)));
    }
}