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

//! Type definitions for archive support

use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use thiserror_no_std::Error;

/// Error type for archive operations
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ArchiveError {
    /// Archive not found
    #[error("Archive not found: {0}")]
    NotFound(String),

    /// Invalid archive format
    #[error("Invalid archive format")]
    InvalidFormat,

    /// Unsupported archive type
    #[error("Unsupported archive type: {0}")]
    UnsupportedType(String),

    /// Entry not found in archive
    #[error("Entry not found: {0}")]
    EntryNotFound(String),

    /// IO error
    #[error("IO error: {0}")]
    IoError(String),

    /// Decompression error
    #[error("Decompression error")]
    DecompressError,

    /// Archive is read-only
    #[error("Archive is read-only")]
    ReadOnly,

    /// Checksum mismatch
    #[error("Checksum mismatch")]
    ChecksumMismatch,
}

/// Archive type
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArchiveType {
    /// ZIP archive
    Zip,
    /// TAR archive
    Tar,
    /// TAR with gzip compression
    TarGz,
    /// TAR with bzip2 compression
    TarBz2,
    /// TAR with xz compression
    TarXz,
    /// 7-Zip archive
    SevenZip,
    /// RAR archive
    Rar,
}

impl ArchiveType {
    /// Get file extension for archive type
    pub fn extension(&self) -> &'static str {
        match self {
            ArchiveType::Zip => "zip",
            ArchiveType::Tar => "tar",
            ArchiveType::TarGz => "tar.gz",
            ArchiveType::TarBz2 => "tar.bz2",
            ArchiveType::TarXz => "tar.xz",
            ArchiveType::SevenZip => "7z",
            ArchiveType::Rar => "rar",
        }
    }

    /// Check if archive type supports writing
    pub fn supports_write(&self) -> bool {
        matches!(self, ArchiveType::Zip | ArchiveType::Tar)
    }
}

/// Compression method used in archive
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionMethod {
    /// No compression (store)
    Store,
    /// Deflate compression
    Deflate,
    /// LZMA compression
    Lzma,
    /// Bzip2 compression
    Bzip2,
    /// Unknown/unsupported compression
    Unknown,
}

/// Archive entry (file or directory)
#[derive(Debug, Clone)]
pub struct ArchiveEntry {
    /// Entry name/path within archive
    pub name: String,
    /// Is this a directory?
    pub is_dir: bool,
    /// Uncompressed size
    pub size: u64,
    /// Compressed size
    pub compressed_size: u64,
    /// Modification time (Unix timestamp)
    pub mtime: u64,
    /// File mode/permissions
    pub mode: u32,
    /// Offset in archive file
    pub offset: u64,
    /// Compression method
    pub compression: CompressionMethod,
    /// CRC32 checksum (for ZIP)
    pub crc32: u32,
}

/// Mounted archive for filesystem access
#[derive(Debug, Clone)]
pub struct ArchiveMount {
    /// Path to archive file
    pub archive_path: String,
    /// Archive type
    pub archive_type: ArchiveType,
    /// Archive entries indexed by path
    pub entries: BTreeMap<String, ArchiveEntry>,
    /// Is archive writable
    pub writable: bool,
}

impl ArchiveMount {
    /// Open an existing archive
    pub fn open(path: &str) -> Result<Self, ArchiveError> {
        // Detect archive type
        let archive_type = super::detect_archive_type(path)
            .ok_or_else(|| ArchiveError::UnsupportedType(path.to_string()))?;

        // Parse archive directory
        let entries = match archive_type {
            ArchiveType::Zip => super::parse_zip_directory(path)?,
            ArchiveType::Tar | ArchiveType::TarGz | ArchiveType::TarBz2 | ArchiveType::TarXz => {
                super::parse_tar_directory(path)?
            }
            _ => return Err(ArchiveError::UnsupportedType(path.to_string())),
        };

        Ok(Self {
            archive_path: path.to_string(),
            archive_type,
            entries,
            writable: archive_type.supports_write(),
        })
    }

    /// Create a new archive
    pub fn create(path: &str, archive_type: ArchiveType) -> Result<Self, ArchiveError> {
        if !archive_type.supports_write() {
            return Err(ArchiveError::ReadOnly);
        }

        Ok(Self {
            archive_path: path.to_string(),
            archive_type,
            entries: BTreeMap::new(),
            writable: true,
        })
    }

    /// List entries in a directory within the archive
    pub fn list(&self, dir_path: &str) -> Vec<&ArchiveEntry> {
        let prefix = if dir_path.is_empty() || dir_path == "/" {
            ""
        } else {
            dir_path.trim_start_matches('/')
        };

        self.entries
            .values()
            .filter(|e| {
                if prefix.is_empty() {
                    // Root directory - get entries without /
                    !e.name.contains('/')
                } else {
                    // Subdirectory - get entries starting with prefix
                    e.name.starts_with(prefix)
                        && e.name[prefix.len()..].starts_with('/')
                        && !e.name[prefix.len() + 1..].contains('/')
                }
            })
            .collect()
    }

    /// Read a file from the archive
    pub fn read_file(&self, path: &str) -> Result<Vec<u8>, ArchiveError> {
        let entry = self
            .entries
            .get(path.trim_start_matches('/'))
            .ok_or_else(|| ArchiveError::EntryNotFound(path.to_string()))?;

        if entry.is_dir {
            return Err(ArchiveError::EntryNotFound(path.to_string()));
        }

        // Read and decompress
        // In production, this would read from the archive file
        let _ = entry;
        Ok(Vec::new())
    }
}

/// Result of archive extraction
#[derive(Debug, Clone, Default)]
pub struct ExtractResult {
    /// Number of files extracted
    pub files_extracted: u64,
    /// Number of directories created
    pub directories_created: u64,
    /// Total bytes extracted
    pub bytes_extracted: u64,
}