1use serde::{Serialize, Serializer};
2use std::path::Path;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum FileCategory {
7 Code,
8 Image,
9 Video,
10 Audio,
11 Archive,
12 Document,
13 Database,
14 Executable,
15 Font,
16 Config,
17 Data,
18 Other,
19}
20
21impl FileCategory {
22 pub fn from_path(path: &Path) -> Self {
24 let ext = match path.extension().and_then(|e| e.to_str()) {
25 Some(e) => e.to_ascii_lowercase(),
26 None => return Self::Other,
27 };
28
29 match ext.as_str() {
30 "rs" | "py" | "js" | "ts" | "tsx" | "jsx" | "go" | "c" | "cpp" | "cc" | "h" | "hpp"
32 | "java" | "kt" | "swift" | "rb" | "php" | "cs" | "scala" | "lua" | "r" | "m"
33 | "mm" | "pl" | "pm" | "sh" | "bash" | "zsh" | "fish" | "ps1" | "bat" | "cmd"
34 | "zig" | "asm" | "s" | "v" | "sv" | "vhd" | "vhdl" | "elm" | "ex" | "exs" | "erl"
35 | "hs" | "ml" | "mli" | "clj" | "cljs" | "lisp" | "el" | "dart" | "vue" | "svelte"
36 | "css" | "scss" | "sass" | "less" | "html" | "htm" | "sql" | "graphql" | "gql"
37 | "proto" | "thrift" | "wasm" | "wat" => Self::Code,
38
39 "png" | "jpg" | "jpeg" | "gif" | "bmp" | "svg" | "ico" | "webp" | "tiff" | "tif"
41 | "psd" | "ai" | "eps" | "raw" | "cr2" | "nef" | "arw" | "dng" | "heic" | "heif"
42 | "avif" | "jxl" => Self::Image,
43
44 "mp4" | "mkv" | "avi" | "mov" | "wmv" | "flv" | "webm" | "m4v" | "mpg" | "mpeg"
46 | "3gp" | "ogv" => Self::Video,
47
48 "mp3" | "flac" | "wav" | "aac" | "ogg" | "wma" | "m4a" | "opus" | "aiff" | "ape"
50 | "alac" | "mid" | "midi" => Self::Audio,
51
52 "zip" | "tar" | "gz" | "bz2" | "xz" | "zst" | "lz4" | "7z" | "rar" | "cab" | "iso"
54 | "dmg" | "deb" | "rpm" | "pkg" | "msi" | "appimage" | "snap" | "flatpak" | "tgz"
55 | "tbz2" | "txz" => Self::Archive,
56
57 "pdf" | "doc" | "docx" | "xls" | "xlsx" | "ppt" | "pptx" | "odt" | "ods" | "odp"
59 | "rtf" | "txt" | "md" | "rst" | "tex" | "latex" | "epub" | "mobi" | "pages"
60 | "numbers" | "key" | "csv" => Self::Document,
61
62 "db" | "sqlite" | "sqlite3" | "mdb" | "accdb" | "dbf" | "ldb" => Self::Database,
64
65 "exe" | "dll" | "so" | "dylib" | "a" | "lib" | "o" | "obj" | "class" | "pyc"
67 | "pyo" | "elc" | "beam" => Self::Executable,
68
69 "ttf" | "otf" | "woff" | "woff2" | "eot" => Self::Font,
71
72 "json" | "yaml" | "yml" | "toml" | "ini" | "cfg" | "conf" | "env" | "xml" | "plist"
74 | "properties" | "reg" => Self::Config,
75
76 "bin" | "dat" | "parquet" | "arrow" | "avro" | "msgpack" | "cbor" | "pb" | "npy"
78 | "npz" | "hdf5" | "h5" | "nc" | "fits" => Self::Data,
79
80 _ => Self::Other,
81 }
82 }
83
84 pub const ALL: &[FileCategory] = &[
86 Self::Code,
87 Self::Image,
88 Self::Video,
89 Self::Audio,
90 Self::Archive,
91 Self::Document,
92 Self::Database,
93 Self::Executable,
94 Self::Font,
95 Self::Config,
96 Self::Data,
97 Self::Other,
98 ];
99
100 pub fn rgb(&self) -> (u8, u8, u8) {
103 match self {
104 Self::Code => (86, 156, 214), Self::Image => (206, 145, 52), Self::Video => (214, 86, 86), Self::Audio => (156, 86, 214), Self::Archive => (86, 214, 156), Self::Document => (214, 206, 86), Self::Database => (214, 130, 86), Self::Executable => (180, 86, 86), Self::Font => (150, 150, 150), Self::Config => (120, 180, 120), Self::Data => (86, 136, 214), Self::Other => (100, 100, 120), }
117 }
118
119 pub fn color(&self) -> String {
121 let (r, g, b) = self.rgb();
122 format!("#{r:02x}{g:02x}{b:02x}")
123 }
124
125 pub fn label(&self) -> &'static str {
127 match self {
128 Self::Code => "Code",
129 Self::Image => "Image",
130 Self::Video => "Video",
131 Self::Audio => "Audio",
132 Self::Archive => "Archive",
133 Self::Document => "Document",
134 Self::Database => "Database",
135 Self::Executable => "Executable",
136 Self::Font => "Font",
137 Self::Config => "Config",
138 Self::Data => "Data",
139 Self::Other => "Other",
140 }
141 }
142}
143
144impl Serialize for FileCategory {
146 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
147 serializer.serialize_str(&self.color())
148 }
149}
150
151#[cfg(test)]
152#[path = "category_tests.rs"]
153mod category_tests;