1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[allow(non_camel_case_types)]
9pub enum FileType {
10 Jpeg,
12 Tiff,
13 Png,
14 Gif,
15 Bmp,
16 WebP,
17 Heif,
18 Avif,
19 Psd,
20 Jp2,
21 J2c,
22 Jxl,
23 Jxr,
24 Flif,
25 Bpg,
26 Exr,
27 Ico,
28 Jps,
29 DjVu,
31 Xcf,
32 Pcx,
33 Pict,
34 Psp,
35 Hdr,
36 Rwz,
37 Btf,
38 Mng,
39 PhotoCd,
40 Cr2,
42 Cr3,
43 Crw,
44 Nef,
45 Arw,
46 Sr2,
47 Srf,
48 Orf,
49 Rw2,
50 Dng,
51 Raf,
52 Pef,
53 Dcr,
54 Mrw,
55 Erf,
56 Fff,
57 Iiq,
58 Rwl,
59 Mef,
60 Srw,
61 X3f,
62 Gpr,
63 Arq,
64 ThreeFR,
65 Crm,
66 Mp4,
68 QuickTime,
69 Avi,
70 Mkv,
71 WebM,
72 Wmv,
73 Asf,
74 Flv,
75 Mxf,
76 Czi,
77 M2ts,
78 Mpeg,
79 ThreeGP,
80 RealMedia,
81 R3d,
82 Dvb,
83 Lrv,
84 Mqv,
85 F4v,
86 Wtv,
87 DvrMs,
88 Mp3,
90 Flac,
91 Ogg,
92 Wav,
93 Aiff,
94 Aac,
95 Opus,
96 Mpc,
97 Ape,
98 WavPack,
99 Ofr,
100 Dsf,
101 Audible,
102 RealAudio,
103 Wma,
104 M4a,
105 Dss,
106 Pdf,
108 PostScript,
109 Doc,
110 Docx,
111 Xls,
112 Xlsx,
113 Ppt,
114 Pptx,
115 Numbers,
116 Pages,
117 Key,
118 InDesign,
119 Rtf,
120 Zip,
122 Rar,
123 SevenZ,
124 Gzip,
125 Xmp,
127 Mie,
128 Exv,
129 Vrd,
130 Dr4,
131 Icc,
132 Html,
133 Exe,
134 Font,
135 Swf,
136 Dicom,
137 Fits,
138 Mrc,
140 Moi,
141 MacOs,
142 Json,
143 Pcap,
144 Pcapng,
145 Svg,
146 Pgf,
148 Xisf,
149 Torrent,
150 Mobi,
151 SonyPmp,
152 Plist,
154 Aae,
155 KyoceraRaw,
156 Lfp,
158 PortableFloatMap,
160 Fpf,
162 Ods,
164 Odt,
165 Odp,
166 Odg,
167 Odf,
168 Odb,
169 Odi,
170 Odc,
171 Eip,
173 Lif,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
179pub enum Support {
180 Read,
181 ReadWrite,
182 ReadWriteCreate,
183}
184
185impl FileType {
186 pub fn description(self) -> &'static str {
188 match self {
189 FileType::Jpeg => "JPEG image",
191 FileType::Tiff => "TIFF image",
192 FileType::Png => "PNG image",
193 FileType::Gif => "GIF image",
194 FileType::Bmp => "BMP image",
195 FileType::WebP => "WebP image",
196 FileType::Heif => "HEIF/HEIC image",
197 FileType::Avif => "AVIF image",
198 FileType::Psd => "Adobe Photoshop Document",
199 FileType::Jp2 => "JPEG 2000 image",
200 FileType::J2c => "JPEG 2000 Codestream",
201 FileType::Jxl => "JPEG XL image",
202 FileType::Jxr => "JPEG XR / HD Photo",
203 FileType::Flif => "Free Lossless Image Format",
204 FileType::Bpg => "Better Portable Graphics",
205 FileType::Exr => "OpenEXR image",
206 FileType::Ico => "Windows Icon",
207 FileType::Jps => "JPEG Stereo image",
208 FileType::DjVu => "DjVu document",
210 FileType::Xcf => "GIMP image",
211 FileType::Pcx => "PCX image",
212 FileType::Pict => "Apple PICT",
213 FileType::Psp => "Paint Shop Pro image",
214 FileType::Pgf => "Progressive Graphics File",
215 FileType::Xisf => "PixInsight XISF image",
216 FileType::Torrent => "BitTorrent descriptor",
217 FileType::Mobi => "Mobipocket Book",
218 FileType::SonyPmp => "Sony PMP video",
219 FileType::Plist => "PLIST",
220 FileType::Aae => "AAE",
221 FileType::KyoceraRaw => "Kyocera Contax N RAW",
222 FileType::Lfp => "LFP",
223 FileType::PortableFloatMap => "Portable Float Map",
224 FileType::Hdr => "Radiance HDR",
225 FileType::Rwz => "Rawzor compressed image",
226 FileType::Btf => "BigTIFF image",
227 FileType::Mng => "MNG animation",
228 FileType::PhotoCd => "Kodak Photo CD",
229 FileType::Cr2 => "Canon CR2 RAW",
231 FileType::Cr3 => "Canon CR3 RAW",
232 FileType::Crw => "Canon CRW RAW",
233 FileType::Nef => "Nikon NEF RAW",
234 FileType::Arw => "Sony ARW RAW",
235 FileType::Sr2 => "Sony SR2 RAW",
236 FileType::Srf => "Sony SRF RAW",
237 FileType::Orf => "Olympus ORF RAW",
238 FileType::Rw2 => "Panasonic RW2 RAW",
239 FileType::Dng => "Adobe Digital Negative",
240 FileType::Raf => "Fujifilm RAF RAW",
241 FileType::Pef => "Pentax PEF RAW",
242 FileType::Dcr => "Kodak DCR RAW",
243 FileType::Mrw => "Minolta MRW RAW",
244 FileType::Erf => "Epson ERF RAW",
245 FileType::Fff => "Hasselblad FFF RAW",
246 FileType::Iiq => "Phase One IIQ RAW",
247 FileType::Rwl => "Leica RWL RAW",
248 FileType::Mef => "Mamiya MEF RAW",
249 FileType::Srw => "Samsung SRW RAW",
250 FileType::X3f => "Sigma X3F RAW",
251 FileType::Gpr => "GoPro GPR RAW",
252 FileType::Arq => "Sony ARQ RAW",
253 FileType::ThreeFR => "Hasselblad 3FR RAW",
254 FileType::Crm => "Canon Cinema RAW",
255 FileType::Mp4 => "MP4 video",
257 FileType::QuickTime => "QuickTime video",
258 FileType::Avi => "AVI",
259 FileType::Mkv => "Matroska video",
260 FileType::WebM => "WebM video",
261 FileType::Wmv => "Windows Media Video",
262 FileType::Asf => "Advanced Systems Format",
263 FileType::Flv => "Flash Video",
264 FileType::Mxf => "Material Exchange Format",
265 FileType::Czi => "CZI",
266 FileType::M2ts => "MPEG-2 Transport Stream",
267 FileType::Mpeg => "MPEG video",
268 FileType::ThreeGP => "3GPP multimedia",
269 FileType::RealMedia => "RealMedia",
270 FileType::R3d => "Redcode RAW video",
271 FileType::Dvb => "Digital Video Broadcasting",
272 FileType::Lrv => "GoPro Low-Res Video",
273 FileType::Mqv => "Sony Movie",
274 FileType::F4v => "Adobe Flash Video",
275 FileType::Wtv => "Windows Recorded TV",
276 FileType::DvrMs => "Microsoft DVR",
277 FileType::Mp3 => "MP3 audio",
279 FileType::Flac => "FLAC audio",
280 FileType::Ogg => "Ogg Vorbis audio",
281 FileType::Wav => "WAV audio",
282 FileType::Aiff => "AIFF",
283 FileType::Aac => "AAC audio",
284 FileType::Opus => "Opus audio",
285 FileType::Mpc => "Musepack audio",
286 FileType::Ape => "Monkey's Audio",
287 FileType::WavPack => "WavPack audio",
288 FileType::Ofr => "OptimFROG audio",
289 FileType::Dsf => "DSD Stream File",
290 FileType::Audible => "Audible audiobook",
291 FileType::RealAudio => "RealAudio",
292 FileType::Wma => "Windows Media Audio",
293 FileType::M4a => "MPEG-4 Audio",
294 FileType::Dss => "DSS",
295 FileType::Pdf => "PDF document",
297 FileType::PostScript => "PostScript",
298 FileType::Doc => "Microsoft Word (legacy)",
299 FileType::Docx => "Microsoft Word",
300 FileType::Xls => "Microsoft Excel (legacy)",
301 FileType::Xlsx => "Microsoft Excel",
302 FileType::Ppt => "Microsoft PowerPoint (legacy)",
303 FileType::Pptx => "Microsoft PowerPoint",
304 FileType::Numbers => "Apple Numbers",
305 FileType::Pages => "Apple Pages",
306 FileType::Key => "Apple Keynote",
307 FileType::InDesign => "Adobe InDesign",
308 FileType::Rtf => "Rich Text Format",
309 FileType::Zip => "ZIP archive",
311 FileType::Rar => "RAR archive",
312 FileType::SevenZ => "7-Zip archive",
313 FileType::Gzip => "GZIP",
314 FileType::Xmp => "XMP sidecar",
316 FileType::Mie => "MIE metadata",
317 FileType::Exv => "Exiv2 metadata",
318 FileType::Vrd => "VRD",
319 FileType::Dr4 => "DR4",
320 FileType::Icc => "ICC color profile",
321 FileType::Html => "HTML document",
322 FileType::Exe => "Windows executable",
323 FileType::Font => "Font file",
324 FileType::Swf => "Shockwave Flash",
325 FileType::Dicom => "DICOM medical image",
326 FileType::Fits => "FITS astronomical image",
327 FileType::Mrc => "MRC image",
328 FileType::Moi => "MOI",
329 FileType::MacOs => "MacOS",
330 FileType::Json => "JSON",
331 FileType::Pcap => "PCAP",
332 FileType::Pcapng => "PCAPNG",
333 FileType::Svg => "SVG",
334 FileType::Fpf => "FPF",
335 FileType::Ods => "ODS",
336 FileType::Odt => "ODT",
337 FileType::Odp => "ODP",
338 FileType::Odg => "ODG",
339 FileType::Odf => "ODF",
340 FileType::Odb => "ODB",
341 FileType::Odi => "ODI",
342 FileType::Odc => "ODC",
343 FileType::Eip => "EIP",
344 FileType::Lif => "Leica Image Format",
345 }
346 }
347
348 pub fn mime_type(self) -> &'static str {
350 match self {
351 FileType::Jpeg => "image/jpeg",
352 FileType::Tiff | FileType::Btf => "image/tiff",
353 FileType::Png => "image/png",
354 FileType::Gif => "image/gif",
355 FileType::Bmp => "image/bmp",
356 FileType::WebP => "image/webp",
357 FileType::Heif => "image/heif",
358 FileType::Avif => "image/avif",
359 FileType::Psd => "image/vnd.adobe.photoshop",
360 FileType::Jp2 => "image/jp2",
361 FileType::J2c => "image/x-j2c",
362 FileType::Jxl => "image/jxl",
363 FileType::Jxr => "image/jxr",
364 FileType::Flif => "image/flif",
365 FileType::Bpg => "image/bpg",
366 FileType::Exr => "image/x-exr",
367 FileType::Ico => "image/x-icon",
368 FileType::Jps => "image/x-jps",
369 FileType::DjVu => "image/vnd.djvu",
370 FileType::Xcf => "image/x-xcf",
371 FileType::Pcx => "image/x-pcx",
372 FileType::Pict => "image/x-pict",
373 FileType::Psp => "image/x-psp",
374 FileType::Hdr => "image/vnd.radiance",
375 FileType::Rwz => "image/x-rawzor",
376 FileType::Mng => "video/x-mng",
377 FileType::PhotoCd => "image/x-photo-cd",
378 FileType::Cr2 => "image/x-canon-cr2",
380 FileType::Cr3 | FileType::Crm => "image/x-canon-cr3",
381 FileType::Crw => "image/x-canon-crw",
382 FileType::Nef => "image/x-nikon-nef",
383 FileType::Arw | FileType::Arq => "image/x-sony-arw",
384 FileType::Sr2 => "image/x-sony-sr2",
385 FileType::Srf => "image/x-sony-srf",
386 FileType::Orf => "image/x-olympus-orf",
387 FileType::Rw2 => "image/x-panasonic-rw2",
388 FileType::Dng | FileType::Gpr => "image/x-adobe-dng",
389 FileType::Raf => "image/x-fuji-raf",
390 FileType::Pef => "image/x-pentax-pef",
391 FileType::Dcr => "image/x-kodak-dcr",
392 FileType::Mrw => "image/x-minolta-mrw",
393 FileType::Erf => "image/x-epson-erf",
394 FileType::Fff | FileType::ThreeFR => "image/x-hasselblad-fff",
395 FileType::Iiq => "image/x-phaseone-iiq",
396 FileType::Rwl => "image/x-leica-rwl",
397 FileType::Mef => "image/x-mamiya-mef",
398 FileType::Srw => "image/x-samsung-srw",
399 FileType::X3f => "image/x-sigma-x3f",
400 FileType::Mp4 | FileType::F4v => "video/mp4",
402 FileType::QuickTime | FileType::Mqv => "video/quicktime",
403 FileType::Avi => "video/x-msvideo",
404 FileType::Mkv => "video/x-matroska",
405 FileType::WebM => "video/webm",
406 FileType::Wmv => "video/x-ms-wmv",
407 FileType::Asf => "video/x-ms-asf",
408 FileType::Flv => "video/x-flv",
409 FileType::Mxf => "application/mxf",
410 FileType::Czi => "image/czi",
411 FileType::M2ts => "video/mp2t",
412 FileType::Mpeg => "video/mpeg",
413 FileType::ThreeGP => "video/3gpp",
414 FileType::RealMedia => "application/vnd.rn-realmedia",
415 FileType::R3d => "video/x-red-r3d",
416 FileType::Dvb => "video/dvb",
417 FileType::Lrv => "video/mp4",
418 FileType::Wtv => "video/x-ms-wtv",
419 FileType::DvrMs => "video/x-ms-dvr",
420 FileType::Mp3 => "audio/mpeg",
422 FileType::Flac => "audio/flac",
423 FileType::Ogg | FileType::Opus => "audio/ogg",
424 FileType::Wav => "audio/wav",
425 FileType::Aiff => "audio/x-aiff",
426 FileType::Aac => "audio/aac",
427 FileType::Mpc => "audio/x-musepack",
428 FileType::Ape => "audio/x-ape",
429 FileType::WavPack => "audio/x-wavpack",
430 FileType::Ofr => "audio/x-ofr",
431 FileType::Dsf => "audio/dsf",
432 FileType::Audible => "audio/x-pn-audibleaudio",
433 FileType::RealAudio => "audio/x-pn-realaudio",
434 FileType::Wma => "audio/x-ms-wma",
435 FileType::M4a => "audio/mp4",
436 FileType::Dss => "audio/x-dss",
437 FileType::Pdf => "application/pdf",
439 FileType::PostScript => "application/postscript",
440 FileType::Doc => "application/msword",
441 FileType::Docx => {
442 "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
443 }
444 FileType::Xls => "application/vnd.ms-excel",
445 FileType::Xlsx => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
446 FileType::Ppt => "application/vnd.ms-powerpoint",
447 FileType::Pptx => {
448 "application/vnd.openxmlformats-officedocument.presentationml.presentation"
449 }
450 FileType::Numbers => "application/x-iwork-numbers-sffnumbers",
451 FileType::Pages => "application/x-iwork-pages-sffpages",
452 FileType::Key => "application/x-iwork-keynote-sffkey",
453 FileType::InDesign => "application/x-indesign",
454 FileType::Rtf => "application/rtf",
455 FileType::Zip => "application/zip",
457 FileType::Rar => "application/x-rar-compressed",
458 FileType::SevenZ => "application/x-7z-compressed",
459 FileType::Gzip => "application/x-gzip",
460 FileType::Xmp => "application/rdf+xml",
462 FileType::Mie => "application/x-mie",
463 FileType::Exv => "application/x-exv",
464 FileType::Vrd => "application/octet-stream",
465 FileType::Dr4 => "application/octet-stream",
466 FileType::Icc => "application/vnd.icc.profile",
467 FileType::Html => "text/html",
468 FileType::Exe => "application/x-dosexec",
469 FileType::Font => "font/sfnt",
470 FileType::Swf => "application/x-shockwave-flash",
471 FileType::Dicom => "application/dicom",
472 FileType::Fits => "application/fits",
473 FileType::Mrc => "image/x-mrc",
474 FileType::Moi => "application/octet-stream",
475 FileType::MacOs => "application/unknown",
476 FileType::Json => "application/json",
477 FileType::Pcap => "application/vnd.tcpdump.pcap",
478 FileType::Pcapng => "application/vnd.tcpdump.pcap",
479 FileType::Svg => "image/svg+xml",
480 FileType::Pgf => "image/pgf",
481 FileType::Xisf => "application/xisf",
482 FileType::Torrent => "application/x-bittorrent",
483 FileType::Mobi => "application/x-mobipocket-ebook",
484 FileType::SonyPmp => "image/x-sony-pmp",
485 FileType::Plist => "application/x-plist",
486 FileType::Aae => "application/vnd.apple.photos",
487 FileType::KyoceraRaw => "image/x-raw",
488 FileType::Lfp => "image/x-lytro-lfp",
489 FileType::PortableFloatMap => "image/x-pfm",
490 FileType::Fpf => "image/x-flir-fpf",
491 FileType::Ods => "application/vnd.oasis.opendocument.spreadsheet",
492 FileType::Odt => "application/vnd.oasis.opendocument.text",
493 FileType::Odp => "application/vnd.oasis.opendocument.presentation",
494 FileType::Odg => "application/vnd.oasis.opendocument.graphics",
495 FileType::Odf => "application/vnd.oasis.opendocument.formula",
496 FileType::Odb => "application/vnd.oasis.opendocument.database",
497 FileType::Odi => "application/vnd.oasis.opendocument.image",
498 FileType::Odc => "application/vnd.oasis.opendocument.chart",
499 FileType::Eip => "application/x-captureone",
500 FileType::Lif => "image/x-leica-lif",
501 }
502 }
503
504 pub fn extensions(self) -> &'static [&'static str] {
506 match self {
507 FileType::Jpeg => &["jpg", "jpeg", "jpe", "jif", "jfif"],
508 FileType::Tiff => &["tif", "tiff"],
509 FileType::Png => &["png"],
510 FileType::Gif => &["gif"],
511 FileType::Bmp => &["bmp", "dib"],
512 FileType::WebP => &["webp"],
513 FileType::Heif => &["heif", "heic", "hif"],
514 FileType::Avif => &["avif"],
515 FileType::Psd => &["psd", "psb", "psdt"],
516 FileType::Jp2 => &["jp2", "jpf", "jpm", "jpx", "jph"],
517 FileType::J2c => &["j2c", "j2k", "jpc"],
518 FileType::Jxl => &["jxl"],
519 FileType::Jxr => &["jxr", "hdp", "wdp"],
520 FileType::Flif => &["flif"],
521 FileType::Bpg => &["bpg"],
522 FileType::Exr => &["exr"],
523 FileType::Ico => &["ico", "cur"],
524 FileType::Jps => &["jps"],
525 FileType::DjVu => &["djvu", "djv"],
526 FileType::Xcf => &["xcf"],
527 FileType::Pcx => &["pcx"],
528 FileType::Pict => &["pict", "pct"],
529 FileType::Psp => &["psp", "pspimage"],
530 FileType::Hdr => &["hdr"],
531 FileType::Rwz => &["rwz"],
532 FileType::Btf => &["btf"],
533 FileType::Mng => &["mng", "jng"],
534 FileType::PhotoCd => &["pcd"],
535 FileType::Cr2 => &["cr2"],
537 FileType::Cr3 => &["cr3"],
538 FileType::Crw => &["crw", "ciff"],
539 FileType::Nef => &["nef", "nrw"],
540 FileType::Arw => &["arw"],
541 FileType::Sr2 => &["sr2"],
542 FileType::Srf => &["srf"],
543 FileType::Orf => &["orf", "ori"],
544 FileType::Rw2 => &["rw2"],
545 FileType::Dng => &["dng"],
546 FileType::Raf => &["raf"],
547 FileType::Pef => &["pef"],
548 FileType::Dcr => &["dcr"],
549 FileType::Mrw => &["mrw"],
550 FileType::Erf => &["erf"],
551 FileType::Fff => &["fff"],
552 FileType::Iiq => &["iiq"],
553 FileType::Rwl => &["rwl"],
554 FileType::Mef => &["mef"],
555 FileType::Srw => &["srw"],
556 FileType::X3f => &["x3f"],
557 FileType::Gpr => &["gpr"],
558 FileType::Arq => &["arq"],
559 FileType::ThreeFR => &["3fr"],
560 FileType::Crm => &["crm"],
561 FileType::Mp4 => &["mp4", "m4v"],
563 FileType::QuickTime => &["mov", "qt"],
564 FileType::Avi => &["avi"],
565 FileType::Mkv => &["mkv", "mks"],
566 FileType::WebM => &["webm"],
567 FileType::Wmv => &["wmv"],
568 FileType::Asf => &["asf"],
569 FileType::Flv => &["flv"],
570 FileType::Mxf => &["mxf"],
571 FileType::Czi => &["czi"],
572 FileType::M2ts => &["m2ts", "mts", "m2t", "ts"],
573 FileType::Mpeg => &["mpg", "mpeg", "m2v", "mpv"],
574 FileType::ThreeGP => &["3gp", "3gpp", "3g2", "3gp2"],
575 FileType::RealMedia => &["rm", "rv", "rmvb"],
576 FileType::R3d => &["r3d"],
577 FileType::Dvb => &["dvb"],
578 FileType::Lrv => &["lrv", "lrf"],
579 FileType::Mqv => &["mqv"],
580 FileType::F4v => &["f4v", "f4a", "f4b", "f4p"],
581 FileType::Wtv => &["wtv"],
582 FileType::DvrMs => &["dvr-ms"],
583 FileType::Mp3 => &["mp3"],
585 FileType::Flac => &["flac"],
586 FileType::Ogg => &["ogg", "oga", "ogv"],
587 FileType::Wav => &["wav"],
588 FileType::Aiff => &["aiff", "aif", "aifc"],
589 FileType::Aac => &["aac"],
590 FileType::Opus => &["opus"],
591 FileType::Mpc => &["mpc"],
592 FileType::Ape => &["ape"],
593 FileType::WavPack => &["wv", "wvp"],
594 FileType::Ofr => &["ofr"],
595 FileType::Dsf => &["dsf"],
596 FileType::Audible => &["aa", "aax"],
597 FileType::RealAudio => &["ra"],
598 FileType::Wma => &["wma"],
599 FileType::M4a => &["m4a", "m4b", "m4p"],
600 FileType::Dss => &["dss"],
601 FileType::Pdf => &["pdf"],
603 FileType::PostScript => &["ps", "eps", "epsf"],
604 FileType::Doc => &["doc", "dot"],
605 FileType::Docx => &["docx", "docm"],
606 FileType::Xls => &["xls", "xlt"],
607 FileType::Xlsx => &["xlsx", "xlsm", "xlsb"],
608 FileType::Ppt => &["ppt", "pps", "pot"],
609 FileType::Pptx => &["pptx", "pptm"],
610 FileType::Numbers => &["numbers", "nmbtemplate"],
611 FileType::Pages => &["pages"],
612 FileType::Key => &["key", "kth"],
613 FileType::InDesign => &["ind", "indd", "indt"],
614 FileType::Rtf => &["rtf"],
615 FileType::Zip => &["zip"],
617 FileType::Rar => &["rar"],
618 FileType::SevenZ => &["7z"],
619 FileType::Gzip => &["gz", "gzip"],
620 FileType::Xmp => &["xmp", "inx", "xml"],
622 FileType::Mie => &["mie"],
623 FileType::Exv => &["exv"],
624 FileType::Vrd => &["vrd"],
625 FileType::Dr4 => &["dr4"],
626 FileType::Icc => &["icc", "icm"],
627 FileType::Html => &["html", "htm", "xhtml", "svg"],
628 FileType::Exe => &["exe", "dll", "elf", "so", "dylib", "a", "macho", "o"],
629 FileType::Font => &[
630 "ttf", "otf", "woff", "woff2", "ttc", "dfont", "afm", "pfa", "pfb",
631 ],
632 FileType::Swf => &["swf"],
633 FileType::Dicom => &["dcm"],
634 FileType::Fits => &["fits", "fit", "fts"],
635 FileType::Mrc => &["mrc"],
636 FileType::Moi => &["moi"],
637 FileType::MacOs => &["macos"],
638 FileType::Json => &["json"],
639 FileType::Pcap => &["pcap", "cap"],
640 FileType::Pcapng => &["pcapng", "ntar"],
641 FileType::Svg => &["svg"],
642 FileType::Pgf => &["pgf"],
643 FileType::Xisf => &["xisf"],
644 FileType::Torrent => &["torrent"],
645 FileType::Mobi => &["mobi", "azw", "azw3"],
646 FileType::SonyPmp => &["pmp"],
647 FileType::Plist => &["plist"],
648 FileType::Aae => &["aae"],
649 FileType::KyoceraRaw => &["raw"],
650 FileType::PortableFloatMap => &["pfm"],
651 FileType::Fpf => &["fpf"],
652 FileType::Ods => &["ods"],
653 FileType::Odt => &["odt"],
654 FileType::Odp => &["odp"],
655 FileType::Odg => &["odg"],
656 FileType::Odf => &["odf"],
657 FileType::Odb => &["odb"],
658 FileType::Odi => &["odi"],
659 FileType::Odc => &["odc"],
660 FileType::Lfp => &["lfp", "lfr"],
661 FileType::Eip => &["eip"],
662 FileType::Lif => &["lif"],
663 }
664 }
665
666 pub fn support(self) -> Support {
668 match self {
669 FileType::Xmp | FileType::Mie | FileType::Exv => Support::ReadWriteCreate,
671 FileType::Jpeg
673 | FileType::Tiff
674 | FileType::Png
675 | FileType::Gif
676 | FileType::WebP
677 | FileType::Heif
678 | FileType::Avif
679 | FileType::Psd
680 | FileType::Jp2
681 | FileType::Jxl
682 | FileType::Jxr
683 | FileType::Flif
684 | FileType::Cr2
685 | FileType::Cr3
686 | FileType::Crw
687 | FileType::Nef
688 | FileType::Arw
689 | FileType::Arq
690 | FileType::Sr2
691 | FileType::Orf
692 | FileType::Rw2
693 | FileType::Dng
694 | FileType::Raf
695 | FileType::Pef
696 | FileType::Erf
697 | FileType::Fff
698 | FileType::Iiq
699 | FileType::Rwl
700 | FileType::Mef
701 | FileType::Srw
702 | FileType::X3f
703 | FileType::Gpr
704 | FileType::Crm
705 | FileType::Mp4
706 | FileType::QuickTime
707 | FileType::ThreeGP
708 | FileType::Dvb
709 | FileType::Lrv
710 | FileType::Mqv
711 | FileType::F4v
712 | FileType::Pdf
713 | FileType::PostScript
714 | FileType::InDesign
715 | FileType::Vrd
716 | FileType::Dr4
717 | FileType::Audible => Support::ReadWrite,
718 _ => Support::Read,
720 }
721 }
722
723 pub fn all() -> &'static [FileType] {
725 ALL_FILE_TYPES
726 }
727}
728
729static ALL_FILE_TYPES: &[FileType] = &[
730 FileType::Jpeg,
732 FileType::Tiff,
733 FileType::Png,
734 FileType::Gif,
735 FileType::Bmp,
736 FileType::WebP,
737 FileType::Heif,
738 FileType::Avif,
739 FileType::Psd,
740 FileType::Jp2,
741 FileType::J2c,
742 FileType::Jxl,
743 FileType::Jxr,
744 FileType::Flif,
745 FileType::Bpg,
746 FileType::Exr,
747 FileType::Ico,
748 FileType::Jps,
749 FileType::DjVu,
751 FileType::Xcf,
752 FileType::Pcx,
753 FileType::Pict,
754 FileType::Psp,
755 FileType::Hdr,
756 FileType::Rwz,
757 FileType::Btf,
758 FileType::Mng,
759 FileType::PhotoCd,
760 FileType::Lif,
761 FileType::Cr2,
763 FileType::Cr3,
764 FileType::Crw,
765 FileType::Nef,
766 FileType::Arw,
767 FileType::Sr2,
768 FileType::Srf,
769 FileType::Orf,
770 FileType::Rw2,
771 FileType::Dng,
772 FileType::Raf,
773 FileType::Pef,
774 FileType::Dcr,
775 FileType::Mrw,
776 FileType::Erf,
777 FileType::Fff,
778 FileType::Iiq,
779 FileType::Rwl,
780 FileType::Mef,
781 FileType::Srw,
782 FileType::X3f,
783 FileType::Gpr,
784 FileType::Arq,
785 FileType::ThreeFR,
786 FileType::Crm,
787 FileType::Mp4,
789 FileType::QuickTime,
790 FileType::Avi,
791 FileType::Mkv,
792 FileType::WebM,
793 FileType::Wmv,
794 FileType::Asf,
795 FileType::Flv,
796 FileType::Mxf,
797 FileType::M2ts,
798 FileType::Mpeg,
799 FileType::ThreeGP,
800 FileType::RealMedia,
801 FileType::R3d,
802 FileType::Dvb,
803 FileType::Lrv,
804 FileType::Mqv,
805 FileType::F4v,
806 FileType::Wtv,
807 FileType::DvrMs,
808 FileType::Mp3,
810 FileType::Flac,
811 FileType::Ogg,
812 FileType::Wav,
813 FileType::Aiff,
814 FileType::Aac,
815 FileType::Opus,
816 FileType::Mpc,
817 FileType::Ape,
818 FileType::WavPack,
819 FileType::Ofr,
820 FileType::Dsf,
821 FileType::Audible,
822 FileType::RealAudio,
823 FileType::Wma,
824 FileType::M4a,
825 FileType::Dss,
826 FileType::Pdf,
828 FileType::PostScript,
829 FileType::Doc,
830 FileType::Docx,
831 FileType::Xls,
832 FileType::Xlsx,
833 FileType::Ppt,
834 FileType::Pptx,
835 FileType::Numbers,
836 FileType::Pages,
837 FileType::Key,
838 FileType::InDesign,
839 FileType::Rtf,
840 FileType::Zip,
842 FileType::Rar,
843 FileType::SevenZ,
844 FileType::Gzip,
845 FileType::Xmp,
847 FileType::Mie,
848 FileType::Exv,
849 FileType::Vrd,
850 FileType::Dr4,
851 FileType::Icc,
852 FileType::Html,
853 FileType::Exe,
854 FileType::Font,
855 FileType::Swf,
856 FileType::Dicom,
857 FileType::Fits,
858 FileType::Mrc,
859 FileType::Moi,
860 FileType::MacOs,
861 FileType::Json,
862 FileType::Pcap,
863 FileType::Pcapng,
864 FileType::Svg,
865 FileType::Pgf,
866 FileType::Xisf,
867 FileType::Torrent,
868 FileType::Mobi,
869 FileType::SonyPmp,
870 FileType::Plist,
871 FileType::Aae,
872 FileType::KyoceraRaw,
873 FileType::PortableFloatMap,
874 FileType::Fpf,
875 FileType::Lfp,
876 FileType::Ods,
878 FileType::Odt,
879 FileType::Odp,
880 FileType::Odg,
881 FileType::Odf,
882 FileType::Odb,
883 FileType::Odi,
884 FileType::Odc,
885 FileType::Eip,
887];
888
889pub fn detect_from_magic(header: &[u8]) -> Option<FileType> {
891 if header.len() < 4 {
892 return None;
893 }
894
895 if header.starts_with(&[0xFF, 0xD8, 0xFF]) {
899 return Some(FileType::Jpeg);
900 }
901
902 if header.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
904 return Some(FileType::Png);
905 }
906
907 if header.starts_with(&[0x89, 0x4C, 0x46, 0x50, 0x0D, 0x0A, 0x1A, 0x0A]) {
909 return Some(FileType::Lfp);
910 }
911
912 if header.starts_with(b"GIF8") && header.len() >= 6 && (header[4] == b'7' || header[4] == b'9')
914 {
915 return Some(FileType::Gif);
916 }
917
918 if header.len() >= 8
920 && header.starts_with(b"IIII")
921 && (header[4] == 0x04 || header[4] == 0x05)
922 && header[5] == 0x00
923 && header[6] == 0x04
924 && header[7] == 0x00
925 {
926 return Some(FileType::Dr4);
927 }
928
929 if header.starts_with(b"CANON OPTIONAL DATA\0") {
931 return Some(FileType::Vrd);
932 }
933
934 if header.len() >= 4 {
936 let is_le =
937 header[0] == b'I' && header[1] == b'I' && header[2] == 0x2A && header[3] == 0x00;
938 let is_be =
939 header[0] == b'M' && header[1] == b'M' && header[2] == 0x00 && header[3] == 0x2A;
940 if is_le || is_be {
941 if header.len() >= 10 && is_le && header[8] == b'C' && header[9] == b'R' {
943 return Some(FileType::Cr2);
944 }
945 if header.len() >= 12 && is_le && &header[8..12] == b"IIII" {
947 return Some(FileType::Iiq);
948 }
949 if header.len() >= 12 && is_be && &header[8..12] == b"MMMM" {
950 return Some(FileType::Iiq);
951 }
952 if header.len() >= 4
954 && is_le
955 && header[0] == b'I'
956 && header[1] == b'I'
957 && header.len() >= 8
958 {
959 }
962 return Some(FileType::Tiff);
965 }
966 let is_btf_le =
968 header[0] == b'I' && header[1] == b'I' && header[2] == 0x2B && header[3] == 0x00;
969 let is_btf_be =
970 header[0] == b'M' && header[1] == b'M' && header[2] == 0x00 && header[3] == 0x2B;
971 if is_btf_le || is_btf_be {
972 return Some(FileType::Btf);
973 }
974 }
975
976 if header.starts_with(b"BM") && header.len() >= 6 {
978 return Some(FileType::Bmp);
979 }
980
981 if header.len() >= 12 && header.starts_with(b"RIFF") {
983 match &header[8..12] {
984 b"WEBP" => return Some(FileType::WebP),
985 b"AVI " => return Some(FileType::Avi),
986 b"WAVE" => return Some(FileType::Wav),
987 _ => {}
988 }
989 }
990
991 if header.starts_with(b"8BPS") {
993 return Some(FileType::Psd);
994 }
995
996 if header.len() >= 12 && header.starts_with(&[0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20]) {
998 return Some(FileType::Jp2);
999 }
1000
1001 if header.starts_with(&[0xFF, 0x4F, 0xFF, 0x51]) {
1003 return Some(FileType::J2c);
1004 }
1005
1006 if header.len() >= 2 && header[0] == 0xFF && header[1] == 0x0A {
1008 return Some(FileType::Jxl);
1009 }
1010 if header.len() >= 12 && header.starts_with(&[0x00, 0x00, 0x00, 0x0C, 0x4A, 0x58, 0x4C, 0x20]) {
1011 return Some(FileType::Jxl);
1012 }
1013
1014 if header.starts_with(b"FLIF") {
1016 return Some(FileType::Flif);
1017 }
1018
1019 if header.starts_with(&[0x42, 0x50, 0x47, 0xFB]) {
1021 return Some(FileType::Bpg);
1022 }
1023
1024 if header.starts_with(&[0x76, 0x2F, 0x31, 0x01]) {
1026 return Some(FileType::Exr);
1027 }
1028
1029 if header.len() >= 4
1031 && header[0] == 0
1032 && header[1] == 0
1033 && (header[2] == 1 || header[2] == 2)
1034 && header[3] == 0
1035 {
1036 return Some(FileType::Ico);
1037 }
1038
1039 if header.len() >= 8 && header.starts_with(b"AT&TFORM") {
1041 return Some(FileType::DjVu);
1042 }
1043
1044 if header.starts_with(b"gimp xcf") {
1046 return Some(FileType::Xcf);
1047 }
1048
1049 if header.starts_with(&[0x8A, 0x4D, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
1051 return Some(FileType::Mng);
1052 }
1053
1054 if header.starts_with(&[0x8B, 0x4A, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
1056 return Some(FileType::Mng);
1057 }
1058
1059 if header.len() >= 10 && header.starts_with(b"#?RADIANCE") {
1061 return Some(FileType::Hdr);
1062 }
1063
1064 if header.len() >= 3
1066 && header[0] == b'P'
1067 && (header[1] == b'F' || header[1] == b'f')
1068 && header[2] == b'\n'
1069 {
1070 return Some(FileType::PortableFloatMap);
1071 }
1072
1073 if header.starts_with(b"FPF Public Image Format\0") {
1075 return Some(FileType::Fpf);
1076 }
1077
1078 if header.len() >= 15
1080 && header[0] == 0x70
1081 && header[1] == 0x00
1082 && header[2] == 0x00
1083 && header[3] == 0x00
1084 && header[8] == 0x2A
1085 && header[13] == b'<'
1086 && header[14] == 0x00
1087 {
1088 return Some(FileType::Lif);
1089 }
1090
1091 if header.starts_with(b"rawzor") {
1093 return Some(FileType::Rwz);
1094 }
1095
1096 if header.len() >= 4 && header[0] == b'I' && header[1] == b'I' && header[2] == 0xBC {
1098 return Some(FileType::Jxr);
1099 }
1100
1101 if header.len() >= 15 && header.starts_with(b"FUJIFILMCCD-RAW") {
1105 return Some(FileType::Raf);
1106 }
1107
1108 if header.len() >= 14
1110 && header[0] == b'I'
1111 && header[1] == b'I'
1112 && header[2] == 0x1A
1113 && header[3] == 0x00
1114 && &header[6..14] == b"HEAPCCDR"
1115 {
1116 return Some(FileType::Crw);
1117 }
1118
1119 if header.starts_with(&[0x00, 0x4D, 0x52, 0x4D]) {
1121 return Some(FileType::Mrw);
1122 }
1123
1124 if header.starts_with(b"FOVb") {
1126 return Some(FileType::X3f);
1127 }
1128
1129 if header.len() >= 4
1131 && header[0] == b'I'
1132 && header[1] == b'I'
1133 && header[2] == 0x55
1134 && header[3] == 0x00
1135 {
1136 return Some(FileType::Rw2);
1137 }
1138
1139 if header.len() >= 12 && &header[4..8] == b"ftyp" {
1143 let brand = &header[8..12];
1144 if brand == b"heic"
1146 || brand == b"mif1"
1147 || brand == b"heim"
1148 || brand == b"heis"
1149 || brand == b"msf1"
1150 {
1151 return Some(FileType::Heif);
1152 }
1153 if brand == b"avif" || brand == b"avis" {
1155 return Some(FileType::Avif);
1156 }
1157 if brand == b"crx " {
1159 return Some(FileType::Cr3);
1160 }
1161 if brand == b"qt " {
1163 return Some(FileType::QuickTime);
1164 }
1165 if brand == b"3gp4" || brand == b"3gp5" || brand == b"3gp6" || brand == b"3g2a" {
1167 return Some(FileType::ThreeGP);
1168 }
1169 if brand == b"M4A " || brand == b"M4B " || brand == b"M4P " {
1171 return Some(FileType::M4a);
1172 }
1173 if brand == b"M4V " || brand == b"M4VH" || brand == b"M4VP" {
1174 return Some(FileType::Mp4);
1175 }
1176 if brand == b"F4V " || brand == b"F4P " {
1178 return Some(FileType::F4v);
1179 }
1180 return Some(FileType::Mp4);
1182 }
1183
1184 if header.len() >= 8 {
1186 let atom_type = &header[4..8];
1187 if atom_type == b"moov"
1188 || atom_type == b"mdat"
1189 || atom_type == b"wide"
1190 || atom_type == b"free"
1191 || atom_type == b"pnot"
1192 || atom_type == b"skip"
1193 {
1194 return Some(FileType::QuickTime);
1195 }
1196 }
1197
1198 if header.starts_with(&[0x1A, 0x45, 0xDF, 0xA3]) {
1200 return Some(FileType::Mkv);
1201 }
1203
1204 if header.starts_with(b"FLV\x01") {
1206 return Some(FileType::Flv);
1207 }
1208
1209 if header.len() >= 16 && header.starts_with(&[0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11]) {
1211 return Some(FileType::Asf);
1212 }
1214
1215 if header.len() >= 8 && header.starts_with(&[0x06, 0x0E, 0x2B, 0x34]) {
1217 return Some(FileType::Mxf);
1218 }
1219
1220 if header.len() >= 10 && header.starts_with(b"ZISRAWFILE") {
1222 return Some(FileType::Czi);
1223 }
1224
1225 if header.len() >= 40 && &header[36..40] == b"acsp" {
1227 return Some(FileType::Icc);
1228 }
1229
1230 if header.len() >= 4
1232 && header[0] == 0
1233 && header[1] == 0
1234 && header[2] == 1
1235 && (header[3] == 0xBA || header[3] == 0xBB || (header[3] & 0xF0) == 0xE0)
1236 {
1237 return Some(FileType::Mpeg);
1238 }
1239
1240 if !header.is_empty() && header[0] == 0x47 {
1242 if header.len() >= 376 && header[188] == 0x47 {
1243 return Some(FileType::M2ts);
1244 }
1245 if header.len() >= 384 && header[192] == 0x47 {
1246 return Some(FileType::M2ts);
1247 }
1248 }
1249
1250 if header.starts_with(b".RMF") {
1252 return Some(FileType::RealMedia);
1253 }
1254
1255 if header.starts_with(b"RED1") || header.starts_with(b"RED2") {
1257 return Some(FileType::R3d);
1258 }
1259
1260 if header.starts_with(b"ID3") {
1264 return Some(FileType::Mp3);
1265 }
1266 if header.len() >= 2
1270 && header[0] == 0xFF
1271 && (header[1] == 0xF0 || header[1] == 0xF1 || header[1] == 0xF8 || header[1] == 0xF9)
1272 {
1273 return Some(FileType::Aac);
1274 }
1275 if header.len() >= 2 && header[0] == 0xFF && (header[1] & 0xE0) == 0xE0 {
1277 return Some(FileType::Mp3);
1278 }
1279
1280 if header.starts_with(b"fLaC") {
1282 return Some(FileType::Flac);
1283 }
1284
1285 if header.starts_with(b"OggS") {
1287 return Some(FileType::Ogg);
1288 }
1289
1290 if header.len() >= 12
1292 && header.starts_with(b"FORM")
1293 && (&header[8..12] == b"AIFF" || &header[8..12] == b"AIFC")
1294 {
1295 return Some(FileType::Aiff);
1296 }
1297
1298 if header.starts_with(b"MAC ") {
1300 return Some(FileType::Ape);
1301 }
1302
1303 if header.len() >= 0x20 && &header[0x19..0x20] == b"ARECOYK" {
1305 return Some(FileType::KyoceraRaw);
1306 }
1307
1308 if header.starts_with(b"MP+") || header.starts_with(b"MPCK") {
1310 return Some(FileType::Mpc);
1311 }
1312
1313 if header.starts_with(b"wvpk") {
1315 return Some(FileType::WavPack);
1316 }
1317
1318 if header.starts_with(b"DSD ") {
1320 return Some(FileType::Dsf);
1321 }
1322
1323 if header.starts_with(b"OFR ") {
1325 return Some(FileType::Ofr);
1326 }
1327
1328 if header.len() >= 4
1330 && header[0] == b'.'
1331 && header[1] == b'r'
1332 && header[2] == b'a'
1333 && header[3] == 0xFD
1334 {
1335 return Some(FileType::RealAudio);
1336 }
1337
1338 if header.len() >= 4
1340 && (header[0] == 0x02 || header[0] == 0x03)
1341 && (header[1] == b'd')
1342 && (header[2] == b's')
1343 && (header[3] == b's' || header[3] == b'2')
1344 {
1345 return Some(FileType::Dss);
1346 }
1347
1348 if header.starts_with(b"%PDF-") {
1352 return Some(FileType::Pdf);
1353 }
1354
1355 if header.starts_with(b"%!PS") || header.starts_with(b"%!Adobe") {
1357 return Some(FileType::PostScript);
1358 }
1359
1360 if header.starts_with(&[0xD0, 0xCF, 0x11, 0xE0]) {
1362 return Some(FileType::Doc); }
1364
1365 if header.starts_with(b"{\\rtf") {
1367 return Some(FileType::Rtf);
1368 }
1369
1370 if header.len() >= 8 && header.starts_with(&[0x06, 0x06, 0xED, 0xF5, 0xD8, 0x1D, 0x46, 0xE5]) {
1372 return Some(FileType::InDesign);
1373 }
1374
1375 if header.starts_with(&[0x50, 0x4B, 0x03, 0x04]) {
1379 return Some(FileType::Zip);
1380 }
1382
1383 if header.len() >= 6 && header.starts_with(b"Rar!\x1A\x07") {
1385 return Some(FileType::Rar);
1386 }
1387
1388 if header.len() >= 6 && header.starts_with(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]) {
1390 return Some(FileType::SevenZ);
1391 }
1392
1393 if header.len() >= 2 && header[0] == 0x1F && header[1] == 0x8B {
1395 return Some(FileType::Gzip);
1396 }
1397
1398 if header.len() >= 4
1400 && header[0] == 0x0A
1401 && header[1] == 0x0D
1402 && header[2] == 0x0D
1403 && header[3] == 0x0A
1404 {
1405 return Some(FileType::Pcapng);
1406 }
1407
1408 if header.len() >= 4
1410 && ((header[0] == 0xD4 && header[1] == 0xC3 && header[2] == 0xB2 && header[3] == 0xA1)
1411 || (header[0] == 0xA1 && header[1] == 0xB2 && header[2] == 0xC3 && header[3] == 0xD4))
1412 {
1413 return Some(FileType::Pcap);
1414 }
1415
1416 if header.len() >= 3
1420 && ((header[0] == b'F' || header[0] == b'C' || header[0] == b'Z')
1421 && header[1] == b'W'
1422 && header[2] == b'S')
1423 {
1424 return Some(FileType::Swf);
1425 }
1426
1427 if header.len() >= 132 && &header[128..132] == b"DICM" {
1429 return Some(FileType::Dicom);
1430 }
1431
1432 if header.len() >= 214 {
1434 let ax1 = u32::from_le_bytes([header[64], header[65], header[66], header[67]]);
1435 let ax2 = u32::from_le_bytes([header[68], header[69], header[70], header[71]]);
1436 let ax3 = u32::from_le_bytes([header[72], header[73], header[74], header[75]]);
1437 if (1..=3).contains(&ax1)
1438 && (1..=3).contains(&ax2)
1439 && (1..=3).contains(&ax3)
1440 && &header[208..211] == b"MAP"
1441 {
1442 let ms0 = header[212];
1443 let ms1 = header[213];
1444 if (ms1 == 0x41 || ms1 == 0x44) && ms0 == 0x44 || (ms0 == 0x11 && ms1 == 0x11) {
1445 return Some(FileType::Mrc);
1446 }
1447 }
1448 }
1449
1450 if header.len() >= 9 && header.starts_with(b"SIMPLE =") {
1452 return Some(FileType::Fits);
1453 }
1454
1455 if header.len() >= 4 && header[0] == 0x7E && header[1] == 0x10 && header[2] == 0x04 {
1457 return Some(FileType::Mie);
1458 }
1459
1460 if header.starts_with(b"<?xpacket") || header.starts_with(b"<x:xmpmeta") {
1462 return Some(FileType::Xmp);
1463 }
1464
1465 if header.starts_with(b"<?xml") || header.starts_with(b"<svg") {
1467 let preview = &header[..header.len().min(512)];
1468 if preview.windows(4).any(|w| w == b"<svg") {
1469 return Some(FileType::Svg);
1470 }
1471 if preview.windows(5).any(|w| w == b"<html" || w == b"<HTML") {
1472 return Some(FileType::Html); }
1474 if preview.windows(10).any(|w| w == b"<x:xmpmeta")
1475 || preview.windows(9).any(|w| w == b"<?xpacket")
1476 {
1477 return Some(FileType::Xmp);
1478 }
1479 if preview.windows(4).any(|w| w == b"<rdf" || w == b"<RDF") {
1480 return Some(FileType::Xmp);
1481 }
1482 if preview.windows(7).any(|w| w == b"<plist")
1484 || preview.windows(20).any(|w| w == b"DTD PLIST")
1485 {
1486 return Some(FileType::Plist);
1487 }
1488 return Some(FileType::Xmp);
1490 }
1491
1492 if header.starts_with(b"<!DOCTYPE html")
1494 || header.starts_with(b"<!doctype html")
1495 || header.starts_with(b"<!DOCTYPE HTML")
1496 || header.starts_with(b"<html")
1497 || header.starts_with(b"<HTML")
1498 {
1499 return Some(FileType::Html);
1500 }
1501
1502 if header.starts_with(&[0x7F, b'E', b'L', b'F']) {
1504 return Some(FileType::Exe);
1505 }
1506
1507 if header.starts_with(&[0xFE, 0xED, 0xFA, 0xCE])
1509 || header.starts_with(&[0xCE, 0xFA, 0xED, 0xFE])
1510 {
1511 return Some(FileType::Exe);
1512 }
1513
1514 if header.starts_with(&[0xFE, 0xED, 0xFA, 0xCF])
1516 || header.starts_with(&[0xCF, 0xFA, 0xED, 0xFE])
1517 {
1518 return Some(FileType::Exe);
1519 }
1520
1521 if header.starts_with(&[0xCA, 0xFE, 0xBA, 0xBE]) {
1523 return Some(FileType::Exe);
1524 }
1525
1526 if header.starts_with(b"MZ") {
1528 return Some(FileType::Exe);
1529 }
1530
1531 if (header.starts_with(&[0x00, 0x01, 0x00, 0x00])
1533 || header.starts_with(b"true")
1534 || header.starts_with(b"typ1"))
1535 && header.len() >= 12
1536 {
1537 return Some(FileType::Font);
1538 }
1539
1540 if header.starts_with(b"ttcf") {
1542 return Some(FileType::Font);
1543 }
1544
1545 if header.starts_with(b"OTTO") {
1547 return Some(FileType::Font);
1548 }
1549
1550 if header.starts_with(b"wOFF") {
1552 return Some(FileType::Font);
1553 }
1554
1555 if header.starts_with(b"wOF2") {
1557 return Some(FileType::Font);
1558 }
1559
1560 if header.len() >= 2 && header[0] == b'V' && header[1] == b'6' {
1562 return Some(FileType::Moi);
1563 }
1564
1565 if header.starts_with(b"PGF") {
1567 return Some(FileType::Pgf);
1568 }
1569
1570 if header.starts_with(b"XISF0100") {
1572 return Some(FileType::Xisf);
1573 }
1574
1575 if header.len() >= 27 && header.starts_with(b"Paint Shop Pro Image File\n\x1a") {
1577 return Some(FileType::Psp);
1578 }
1579
1580 if header.len() >= 12
1583 && header[8] == 0x00
1584 && header[9] == 0x00
1585 && header[10] == 0x00
1586 && header[11] == 0x7C
1587 {
1588 return Some(FileType::SonyPmp);
1589 }
1590
1591 None
1592}
1593
1594pub fn detect_from_extension(ext: &str) -> Option<FileType> {
1596 let ext_lower = ext.to_ascii_lowercase();
1597 let ext_lower = ext_lower.trim_start_matches('.');
1598
1599 for &ft in ALL_FILE_TYPES {
1600 for known_ext in ft.extensions() {
1601 if ext_lower == *known_ext {
1602 return Some(ft);
1603 }
1604 }
1605 }
1606
1607 None
1608}
1609
1610impl std::fmt::Display for FileType {
1611 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1612 write!(f, "{}", self.description())
1613 }
1614}
1615
1616impl std::fmt::Display for Support {
1617 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1618 match self {
1619 Support::Read => write!(f, "R"),
1620 Support::ReadWrite => write!(f, "R/W"),
1621 Support::ReadWriteCreate => write!(f, "R/W/C"),
1622 }
1623 }
1624}
1625
1626#[cfg(test)]
1627mod tests {
1628 use super::*;
1629
1630 #[test]
1631 fn test_detect_jpeg() {
1632 let header = [0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, b'J', b'F', b'I', b'F'];
1633 assert_eq!(detect_from_magic(&header), Some(FileType::Jpeg));
1634 }
1635
1636 #[test]
1637 fn test_detect_png() {
1638 let header = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
1639 assert_eq!(detect_from_magic(&header), Some(FileType::Png));
1640 }
1641
1642 #[test]
1643 fn test_detect_tiff_le() {
1644 let header = [b'I', b'I', 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00];
1645 assert_eq!(detect_from_magic(&header), Some(FileType::Tiff));
1646 }
1647
1648 #[test]
1649 fn test_detect_tiff_be() {
1650 let header = [b'M', b'M', 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08];
1651 assert_eq!(detect_from_magic(&header), Some(FileType::Tiff));
1652 }
1653
1654 #[test]
1655 fn test_detect_cr2() {
1656 let header = [b'I', b'I', 0x2A, 0x00, 0x10, 0x00, 0x00, 0x00, b'C', b'R'];
1657 assert_eq!(detect_from_magic(&header), Some(FileType::Cr2));
1658 }
1659
1660 #[test]
1661 fn test_detect_pdf() {
1662 let header = b"%PDF-1.7 some more data here";
1663 assert_eq!(detect_from_magic(header), Some(FileType::Pdf));
1664 }
1665
1666 #[test]
1667 fn test_detect_webp() {
1668 let header = b"RIFF\x00\x00\x00\x00WEBP";
1669 assert_eq!(detect_from_magic(header), Some(FileType::WebP));
1670 }
1671
1672 #[test]
1673 fn test_detect_heif() {
1674 let mut header = [0u8; 16];
1675 header[4..8].copy_from_slice(b"ftyp");
1676 header[8..12].copy_from_slice(b"heic");
1677 assert_eq!(detect_from_magic(&header), Some(FileType::Heif));
1678 }
1679
1680 #[test]
1681 fn test_detect_avif() {
1682 let mut header = [0u8; 16];
1683 header[4..8].copy_from_slice(b"ftyp");
1684 header[8..12].copy_from_slice(b"avif");
1685 assert_eq!(detect_from_magic(&header), Some(FileType::Avif));
1686 }
1687
1688 #[test]
1689 fn test_detect_cr3() {
1690 let mut header = [0u8; 16];
1691 header[4..8].copy_from_slice(b"ftyp");
1692 header[8..12].copy_from_slice(b"crx ");
1693 assert_eq!(detect_from_magic(&header), Some(FileType::Cr3));
1694 }
1695
1696 #[test]
1697 fn test_detect_flac() {
1698 assert_eq!(detect_from_magic(b"fLaC\x00\x00"), Some(FileType::Flac));
1699 }
1700
1701 #[test]
1702 fn test_detect_ogg() {
1703 assert_eq!(detect_from_magic(b"OggS\x00\x02"), Some(FileType::Ogg));
1704 }
1705
1706 #[test]
1707 fn test_detect_mp3_id3() {
1708 assert_eq!(detect_from_magic(b"ID3\x04\x00"), Some(FileType::Mp3));
1709 }
1710
1711 #[test]
1712 fn test_detect_rar() {
1713 assert_eq!(
1714 detect_from_magic(b"Rar!\x1A\x07\x01\x00"),
1715 Some(FileType::Rar)
1716 );
1717 }
1718
1719 #[test]
1720 fn test_detect_7z() {
1721 assert_eq!(
1722 detect_from_magic(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]),
1723 Some(FileType::SevenZ)
1724 );
1725 }
1726
1727 #[test]
1728 fn test_detect_gzip() {
1729 assert_eq!(
1730 detect_from_magic(&[0x1F, 0x8B, 0x08, 0x00]),
1731 Some(FileType::Gzip)
1732 );
1733 }
1734
1735 #[test]
1736 fn test_detect_raf() {
1737 assert_eq!(
1738 detect_from_magic(b"FUJIFILMCCD-RAW 0201"),
1739 Some(FileType::Raf)
1740 );
1741 }
1742
1743 #[test]
1744 fn test_detect_psd() {
1745 assert_eq!(detect_from_magic(b"8BPS\x00\x01"), Some(FileType::Psd));
1746 }
1747
1748 #[test]
1749 fn test_detect_from_extension() {
1750 assert_eq!(detect_from_extension("jpg"), Some(FileType::Jpeg));
1751 assert_eq!(detect_from_extension(".JPEG"), Some(FileType::Jpeg));
1752 assert_eq!(detect_from_extension("cr2"), Some(FileType::Cr2));
1753 assert_eq!(detect_from_extension("cr3"), Some(FileType::Cr3));
1754 assert_eq!(detect_from_extension("nef"), Some(FileType::Nef));
1755 assert_eq!(detect_from_extension("arw"), Some(FileType::Arw));
1756 assert_eq!(detect_from_extension("dng"), Some(FileType::Dng));
1757 assert_eq!(detect_from_extension("raf"), Some(FileType::Raf));
1758 assert_eq!(detect_from_extension("mp4"), Some(FileType::Mp4));
1759 assert_eq!(detect_from_extension("mov"), Some(FileType::QuickTime));
1760 assert_eq!(detect_from_extension("flac"), Some(FileType::Flac));
1761 assert_eq!(detect_from_extension("docx"), Some(FileType::Docx));
1762 assert_eq!(detect_from_extension("xlsx"), Some(FileType::Xlsx));
1763 assert_eq!(detect_from_extension("3fr"), Some(FileType::ThreeFR));
1764 assert_eq!(detect_from_extension("xyz"), None);
1765 }
1766
1767 #[test]
1768 fn test_all_types_have_extensions() {
1769 for &ft in FileType::all() {
1770 assert!(!ft.extensions().is_empty(), "{:?} has no extensions", ft);
1771 }
1772 }
1773
1774 #[test]
1775 fn test_all_types_have_mime() {
1776 for &ft in FileType::all() {
1777 assert!(!ft.mime_type().is_empty(), "{:?} has no MIME type", ft);
1778 }
1779 }
1780
1781 #[test]
1782 fn test_total_format_count() {
1783 assert!(
1784 FileType::all().len() >= 100,
1785 "Expected 100+ formats, got {}",
1786 FileType::all().len()
1787 );
1788 }
1789
1790 #[test]
1791 fn test_detect_mp4_ftyp_isom() {
1792 let mut header = [0u8; 16];
1793 header[0..4].copy_from_slice(&[0x00, 0x00, 0x00, 0x10]);
1795 header[4..8].copy_from_slice(b"ftyp");
1796 header[8..12].copy_from_slice(b"isom");
1797 assert_eq!(detect_from_magic(&header), Some(FileType::Mp4));
1798 }
1799
1800 #[test]
1801 fn test_detect_mp4_ftyp_mp42() {
1802 let mut header = [0u8; 16];
1803 header[0..4].copy_from_slice(&[0x00, 0x00, 0x00, 0x10]);
1804 header[4..8].copy_from_slice(b"ftyp");
1805 header[8..12].copy_from_slice(b"mp42");
1806 assert_eq!(detect_from_magic(&header), Some(FileType::Mp4));
1807 }
1808
1809 #[test]
1810 fn test_detect_mkv_ebml() {
1811 let header = [0x1A, 0x45, 0xDF, 0xA3, 0x93, 0x42, 0x82, 0x88];
1813 assert_eq!(detect_from_magic(&header), Some(FileType::Mkv));
1814 }
1815
1816 #[test]
1817 fn test_detect_flif() {
1818 assert_eq!(
1819 detect_from_magic(b"FLIF\x30\x00\x01\x00"),
1820 Some(FileType::Flif)
1821 );
1822 }
1823
1824 #[test]
1825 fn test_detect_bpg() {
1826 assert_eq!(
1827 detect_from_magic(&[0x42, 0x50, 0x47, 0xFB, 0x00, 0x00]),
1828 Some(FileType::Bpg)
1829 );
1830 }
1831
1832 #[test]
1833 fn test_detect_exe_mz() {
1834 assert_eq!(
1836 detect_from_magic(b"MZ\x90\x00\x03\x00\x00\x00"),
1837 Some(FileType::Exe)
1838 );
1839 }
1840
1841 #[test]
1842 fn test_detect_elf() {
1843 assert_eq!(
1844 detect_from_magic(&[0x7F, b'E', b'L', b'F', 0x02, 0x01]),
1845 Some(FileType::Exe)
1846 );
1847 }
1848
1849 #[test]
1850 fn test_detect_macho_64_le() {
1851 assert_eq!(
1853 detect_from_magic(&[0xCF, 0xFA, 0xED, 0xFE, 0x07, 0x00]),
1854 Some(FileType::Exe)
1855 );
1856 }
1857
1858 #[test]
1859 fn test_detect_macho_32_be() {
1860 assert_eq!(
1862 detect_from_magic(&[0xFE, 0xED, 0xFA, 0xCE, 0x00, 0x00]),
1863 Some(FileType::Exe)
1864 );
1865 }
1866
1867 #[test]
1868 fn test_detect_macho_fat() {
1869 assert_eq!(
1871 detect_from_magic(&[0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00]),
1872 Some(FileType::Exe)
1873 );
1874 }
1875
1876 #[test]
1877 fn test_detect_dicom() {
1878 let mut header = vec![0u8; 136];
1880 header[128..132].copy_from_slice(b"DICM");
1881 assert_eq!(detect_from_magic(&header), Some(FileType::Dicom));
1882 }
1883}