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

//! Archive mounting and VFS integration

use alloc::string::{String, ToString};

use super::ARCHIVE_CACHE;
use super::types::{ArchiveError, ArchiveType};

/// Check if a path points inside an archive
pub fn resolve_archive_path(path: &str) -> Option<ResolvedPath> {
    let components: alloc::vec::Vec<&str> = path.split('/').collect();

    for i in 1..components.len() {
        let potential_archive = components[..i].join("/");

        if let Some(archive_type) = super::detect_archive_type(&potential_archive) {
            let inner_path = components[i..].join("/");
            return Some(ResolvedPath::InArchive {
                archive_path: potential_archive,
                archive_type,
                inner_path,
            });
        }
    }

    None
}

/// Resolved path (normal or inside archive)
#[derive(Debug, Clone)]
pub enum ResolvedPath {
    /// Normal filesystem path
    Normal(String),
    /// Path inside an archive
    InArchive {
        /// Path to the archive file
        archive_path: String,
        /// Type of archive
        archive_type: ArchiveType,
        /// Path within the archive
        inner_path: String,
    },
}

/// Get or open an archive (with caching)
pub fn get_or_open_archive(path: &str) -> Result<super::types::ArchiveMount, ArchiveError> {
    {
        let cache = ARCHIVE_CACHE.lock();
        if let Some(mount) = cache.get(path) {
            return Ok(mount.clone());
        }
    }

    // Open archive
    let mount = super::types::ArchiveMount::open(path)?;

    // Cache it
    {
        let mut cache = ARCHIVE_CACHE.lock();
        cache.insert(path.to_string(), mount.clone());
    }

    Ok(mount)
}

/// Evict an archive from cache
pub fn evict_archive(path: &str) {
    let mut cache = ARCHIVE_CACHE.lock();
    cache.remove(path);
}

/// Clear archive cache
pub fn clear_archive_cache() {
    let mut cache = ARCHIVE_CACHE.lock();
    cache.clear();
}

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

    #[test]
    fn test_resolve_normal_path() {
        assert!(resolve_archive_path("/data/file.txt").is_none());
    }

    #[test]
    fn test_resolve_archive_path() {
        let resolved = resolve_archive_path("/data/archive.zip/inner/file.txt");
        assert!(resolved.is_some());
        if let Some(ResolvedPath::InArchive {
            archive_path,
            inner_path,
            ..
        }) = resolved
        {
            assert_eq!(archive_path, "/data/archive.zip");
            assert_eq!(inner_path, "inner/file.txt");
        }
    }
}