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 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}