Skip to main content

robinpath_modules/modules/
mime_mod.rs

1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4    rp.register_builtin("mime.lookup", |args, _| {
5        let ext = args.first().map(|v| v.to_display_string()).unwrap_or_default();
6        let ext = ext.trim_start_matches('.').to_lowercase();
7        Ok(Value::String(
8            ext_to_mime(&ext).unwrap_or("application/octet-stream").to_string(),
9        ))
10    });
11
12    rp.register_builtin("mime.extension", |args, _| {
13        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
14        Ok(match mime_to_ext(&mime) {
15            Some(ext) => Value::String(ext.to_string()),
16            None => Value::Null,
17        })
18    });
19
20    rp.register_builtin("mime.isText", |args, _| {
21        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
22        Ok(Value::Bool(
23            mime.starts_with("text/")
24                || mime.contains("json")
25                || mime.contains("xml")
26                || mime.contains("javascript")
27                || mime.contains("yaml"),
28        ))
29    });
30
31    rp.register_builtin("mime.isImage", |args, _| {
32        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
33        Ok(Value::Bool(mime.starts_with("image/")))
34    });
35
36    rp.register_builtin("mime.isAudio", |args, _| {
37        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
38        Ok(Value::Bool(mime.starts_with("audio/")))
39    });
40
41    rp.register_builtin("mime.isVideo", |args, _| {
42        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
43        Ok(Value::Bool(mime.starts_with("video/")))
44    });
45
46    rp.register_builtin("mime.isFont", |args, _| {
47        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
48        Ok(Value::Bool(mime.starts_with("font/") || mime.contains("font")))
49    });
50
51    rp.register_builtin("mime.isArchive", |args, _| {
52        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
53        let archive_types = [
54            "application/zip",
55            "application/gzip",
56            "application/x-tar",
57            "application/x-rar-compressed",
58            "application/x-7z-compressed",
59            "application/x-bzip2",
60        ];
61        Ok(Value::Bool(archive_types.contains(&mime.as_str())))
62    });
63
64    rp.register_builtin("mime.charset", |args, _| {
65        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
66        if mime.starts_with("text/") || mime.contains("json") || mime.contains("xml") || mime.contains("javascript") {
67            Ok(Value::String("UTF-8".to_string()))
68        } else {
69            Ok(Value::Null)
70        }
71    });
72
73    rp.register_builtin("mime.contentType", |args, _| {
74        let mime = args.first().map(|v| v.to_display_string()).unwrap_or_default();
75        if mime.starts_with("text/") || mime.contains("json") || mime.contains("xml") || mime.contains("javascript") {
76            Ok(Value::String(format!("{}; charset=utf-8", mime)))
77        } else {
78            Ok(Value::String(mime))
79        }
80    });
81
82    rp.register_builtin("mime.detect", |args, _| {
83        let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
84        // Detect by file extension from path
85        if let Some(ext) = std::path::Path::new(&path).extension() {
86            let ext_str = ext.to_string_lossy().to_lowercase();
87            Ok(Value::String(
88                ext_to_mime(&ext_str).unwrap_or("application/octet-stream").to_string(),
89            ))
90        } else {
91            Ok(Value::String("application/octet-stream".to_string()))
92        }
93    });
94
95    rp.register_builtin("mime.allTypes", |_args, _| {
96        let mut obj = indexmap::IndexMap::new();
97        for (ext, mime) in MIME_TABLE {
98            obj.insert(ext.to_string(), Value::String(mime.to_string()));
99        }
100        Ok(Value::Object(obj))
101    });
102}
103
104const MIME_TABLE: &[(&str, &str)] = &[
105    ("html", "text/html"),
106    ("htm", "text/html"),
107    ("css", "text/css"),
108    ("js", "application/javascript"),
109    ("mjs", "application/javascript"),
110    ("json", "application/json"),
111    ("xml", "application/xml"),
112    ("txt", "text/plain"),
113    ("csv", "text/csv"),
114    ("md", "text/markdown"),
115    ("yaml", "application/x-yaml"),
116    ("yml", "application/x-yaml"),
117    ("toml", "application/toml"),
118    ("png", "image/png"),
119    ("jpg", "image/jpeg"),
120    ("jpeg", "image/jpeg"),
121    ("gif", "image/gif"),
122    ("svg", "image/svg+xml"),
123    ("webp", "image/webp"),
124    ("ico", "image/x-icon"),
125    ("bmp", "image/bmp"),
126    ("tiff", "image/tiff"),
127    ("avif", "image/avif"),
128    ("mp3", "audio/mpeg"),
129    ("wav", "audio/wav"),
130    ("ogg", "audio/ogg"),
131    ("flac", "audio/flac"),
132    ("aac", "audio/aac"),
133    ("mp4", "video/mp4"),
134    ("webm", "video/webm"),
135    ("avi", "video/x-msvideo"),
136    ("mkv", "video/x-matroska"),
137    ("mov", "video/quicktime"),
138    ("pdf", "application/pdf"),
139    ("zip", "application/zip"),
140    ("gz", "application/gzip"),
141    ("tar", "application/x-tar"),
142    ("rar", "application/x-rar-compressed"),
143    ("7z", "application/x-7z-compressed"),
144    ("woff", "font/woff"),
145    ("woff2", "font/woff2"),
146    ("ttf", "font/ttf"),
147    ("otf", "font/otf"),
148    ("eot", "application/vnd.ms-fontobject"),
149    ("wasm", "application/wasm"),
150    ("doc", "application/msword"),
151    ("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
152    ("xls", "application/vnd.ms-excel"),
153    ("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
154    ("ppt", "application/vnd.ms-powerpoint"),
155    ("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"),
156    ("ts", "application/typescript"),
157    ("tsx", "application/typescript"),
158    ("jsx", "application/javascript"),
159    ("rs", "text/x-rust"),
160    ("go", "text/x-go"),
161    ("py", "text/x-python"),
162    ("rb", "text/x-ruby"),
163    ("java", "text/x-java"),
164    ("c", "text/x-c"),
165    ("cpp", "text/x-c++"),
166    ("h", "text/x-c"),
167    ("sh", "application/x-sh"),
168    ("bat", "application/x-msdos-program"),
169    ("sql", "application/sql"),
170];
171
172fn ext_to_mime(ext: &str) -> Option<&'static str> {
173    MIME_TABLE.iter().find(|(e, _)| *e == ext).map(|(_, m)| *m)
174}
175
176fn mime_to_ext(mime: &str) -> Option<&'static str> {
177    MIME_TABLE.iter().find(|(_, m)| *m == mime).map(|(e, _)| *e)
178}