fast-fs 0.2.1

High-speed async file system traversal library with batteries-included file browser component
Documentation
// <FILE>crates/fast-fs/src/nav/file_category.rs</FILE> - <DESC>File type categorization</DESC>
// <VERS>VERSION: 0.1.0</VERS>
// <WCTX>Implementing nav module PRD</WCTX>
// <CLOG>Initial creation with extension mapping</CLOG>

//! File category for UI icons and grouping
//!
//! FileCategory provides a simple way to categorize files for UI purposes.
//! Categories are determined by extension, with special handling for
//! directories, symlinks, and executables.

/// File categories for UI icons and grouping
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum FileCategory {
    /// Directory (navigable)
    Directory,
    /// Plain text files (.txt, .md, .log, .json, .yaml, etc.)
    Text,
    /// Source code files (.rs, .py, .js, etc.)
    Code,
    /// Image files (.png, .jpg, .svg, etc.)
    Image,
    /// Video files (.mp4, .mkv, .avi, etc.)
    Video,
    /// Audio files (.mp3, .wav, .flac, etc.)
    Audio,
    /// Archive files (.zip, .tar, .gz, etc.)
    Archive,
    /// Document files (.pdf, .doc, .xlsx, etc.)
    Document,
    /// Executable files (.exe on Windows, +x on Unix)
    Executable,
    /// Symbolic link
    Symlink,
    /// Unknown file type
    #[default]
    Unknown,
}

impl FileCategory {
    /// Whether this category represents a navigable directory
    pub fn is_navigable(&self) -> bool {
        matches!(self, Self::Directory)
    }

    /// Determine category from file extension
    pub fn from_extension(ext: &str) -> Self {
        match ext.to_lowercase().as_str() {
            // Text
            "txt" | "md" | "log" | "csv" | "ini" | "cfg" => Self::Text,

            // Data (treated as Text for simplicity)
            "json" | "yaml" | "yml" | "toml" | "xml" | "html" | "css" => Self::Text,

            // Code
            "rs" | "py" | "js" | "ts" | "tsx" | "jsx" | "go" | "c" | "h" | "cpp" | "hpp"
            | "java" | "kt" | "swift" | "rb" | "php" | "sh" | "bash" | "zsh" | "fish" | "lua"
            | "pl" | "r" | "sql" | "zig" | "nim" | "v" | "hs" | "ml" | "ex" | "exs" | "clj"
            | "scala" => Self::Code,

            // Image
            "png" | "jpg" | "jpeg" | "gif" | "svg" | "webp" | "bmp" | "ico" | "tiff" | "tif"
            | "psd" | "ai" | "raw" | "heic" | "avif" => Self::Image,

            // Video
            "mp4" | "mkv" | "avi" | "mov" | "webm" | "flv" | "wmv" | "m4v" => Self::Video,

            // Audio
            "mp3" | "wav" | "flac" | "ogg" | "m4a" | "aac" | "wma" | "opus" => Self::Audio,

            // Archive
            "zip" | "tar" | "gz" | "bz2" | "xz" | "7z" | "rar" | "zst" | "lz4" => Self::Archive,

            // Document
            "pdf" | "doc" | "docx" | "xls" | "xlsx" | "ppt" | "pptx" | "odt" | "ods" | "odp"
            | "rtf" | "epub" => Self::Document,

            // Executable (Windows)
            "exe" | "bat" | "cmd" | "ps1" | "com" | "msi" => Self::Executable,

            _ => Self::Unknown,
        }
    }
}

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

    #[test]
    fn test_directory_is_navigable() {
        assert!(FileCategory::Directory.is_navigable());
        assert!(!FileCategory::Text.is_navigable());
        assert!(!FileCategory::Code.is_navigable());
    }

    #[test]
    fn test_text_extensions() {
        assert_eq!(FileCategory::from_extension("txt"), FileCategory::Text);
        assert_eq!(FileCategory::from_extension("md"), FileCategory::Text);
        assert_eq!(FileCategory::from_extension("json"), FileCategory::Text);
        assert_eq!(FileCategory::from_extension("yaml"), FileCategory::Text);
        assert_eq!(FileCategory::from_extension("YAML"), FileCategory::Text); // Case insensitive
    }

    #[test]
    fn test_code_extensions() {
        assert_eq!(FileCategory::from_extension("rs"), FileCategory::Code);
        assert_eq!(FileCategory::from_extension("py"), FileCategory::Code);
        assert_eq!(FileCategory::from_extension("js"), FileCategory::Code);
        assert_eq!(FileCategory::from_extension("tsx"), FileCategory::Code);
        assert_eq!(FileCategory::from_extension("go"), FileCategory::Code);
    }

    #[test]
    fn test_image_extensions() {
        assert_eq!(FileCategory::from_extension("png"), FileCategory::Image);
        assert_eq!(FileCategory::from_extension("jpg"), FileCategory::Image);
        assert_eq!(FileCategory::from_extension("svg"), FileCategory::Image);
        assert_eq!(FileCategory::from_extension("PNG"), FileCategory::Image);
    }

    #[test]
    fn test_video_extensions() {
        assert_eq!(FileCategory::from_extension("mp4"), FileCategory::Video);
        assert_eq!(FileCategory::from_extension("mkv"), FileCategory::Video);
        assert_eq!(FileCategory::from_extension("webm"), FileCategory::Video);
    }

    #[test]
    fn test_audio_extensions() {
        assert_eq!(FileCategory::from_extension("mp3"), FileCategory::Audio);
        assert_eq!(FileCategory::from_extension("wav"), FileCategory::Audio);
        assert_eq!(FileCategory::from_extension("flac"), FileCategory::Audio);
    }

    #[test]
    fn test_archive_extensions() {
        assert_eq!(FileCategory::from_extension("zip"), FileCategory::Archive);
        assert_eq!(FileCategory::from_extension("tar"), FileCategory::Archive);
        assert_eq!(FileCategory::from_extension("gz"), FileCategory::Archive);
    }

    #[test]
    fn test_document_extensions() {
        assert_eq!(FileCategory::from_extension("pdf"), FileCategory::Document);
        assert_eq!(FileCategory::from_extension("docx"), FileCategory::Document);
        assert_eq!(FileCategory::from_extension("xlsx"), FileCategory::Document);
    }

    #[test]
    fn test_executable_extensions() {
        assert_eq!(
            FileCategory::from_extension("exe"),
            FileCategory::Executable
        );
        assert_eq!(
            FileCategory::from_extension("bat"),
            FileCategory::Executable
        );
        assert_eq!(
            FileCategory::from_extension("ps1"),
            FileCategory::Executable
        );
    }

    #[test]
    fn test_unknown_extension() {
        assert_eq!(FileCategory::from_extension("xyz"), FileCategory::Unknown);
        assert_eq!(FileCategory::from_extension(""), FileCategory::Unknown);
        assert_eq!(
            FileCategory::from_extension("foobar"),
            FileCategory::Unknown
        );
    }

    #[test]
    fn test_default() {
        assert_eq!(FileCategory::default(), FileCategory::Unknown);
    }
}

// <FILE>crates/fast-fs/src/nav/file_category.rs</FILE>
// <VERS>END OF VERSION: 0.1.0</VERS>