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}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub enum Support {
178 Read,
179 ReadWrite,
180 ReadWriteCreate,
181}
182
183impl FileType {
184 pub fn description(self) -> &'static str {
186 match self {
187 FileType::Jpeg => "JPEG image",
189 FileType::Tiff => "TIFF image",
190 FileType::Png => "PNG image",
191 FileType::Gif => "GIF image",
192 FileType::Bmp => "BMP image",
193 FileType::WebP => "WebP image",
194 FileType::Heif => "HEIF/HEIC image",
195 FileType::Avif => "AVIF image",
196 FileType::Psd => "Adobe Photoshop Document",
197 FileType::Jp2 => "JPEG 2000 image",
198 FileType::J2c => "JPEG 2000 Codestream",
199 FileType::Jxl => "JPEG XL image",
200 FileType::Jxr => "JPEG XR / HD Photo",
201 FileType::Flif => "Free Lossless Image Format",
202 FileType::Bpg => "Better Portable Graphics",
203 FileType::Exr => "OpenEXR image",
204 FileType::Ico => "Windows Icon",
205 FileType::Jps => "JPEG Stereo image",
206 FileType::DjVu => "DjVu document",
208 FileType::Xcf => "GIMP image",
209 FileType::Pcx => "PCX image",
210 FileType::Pict => "Apple PICT",
211 FileType::Psp => "Paint Shop Pro image",
212 FileType::Pgf => "Progressive Graphics File",
213 FileType::Xisf => "PixInsight XISF image",
214 FileType::Torrent => "BitTorrent descriptor",
215 FileType::Mobi => "Mobipocket Book",
216 FileType::SonyPmp => "Sony PMP video",
217 FileType::Plist => "PLIST",
218 FileType::Aae => "AAE",
219 FileType::KyoceraRaw => "Kyocera Contax N RAW",
220 FileType::Lfp => "LFP",
221 FileType::PortableFloatMap => "Portable Float Map",
222 FileType::Hdr => "Radiance HDR",
223 FileType::Rwz => "Rawzor compressed image",
224 FileType::Btf => "BigTIFF image",
225 FileType::Mng => "MNG animation",
226 FileType::PhotoCd => "Kodak Photo CD",
227 FileType::Cr2 => "Canon CR2 RAW",
229 FileType::Cr3 => "Canon CR3 RAW",
230 FileType::Crw => "Canon CRW RAW",
231 FileType::Nef => "Nikon NEF RAW",
232 FileType::Arw => "Sony ARW RAW",
233 FileType::Sr2 => "Sony SR2 RAW",
234 FileType::Srf => "Sony SRF RAW",
235 FileType::Orf => "Olympus ORF RAW",
236 FileType::Rw2 => "Panasonic RW2 RAW",
237 FileType::Dng => "Adobe Digital Negative",
238 FileType::Raf => "Fujifilm RAF RAW",
239 FileType::Pef => "Pentax PEF RAW",
240 FileType::Dcr => "Kodak DCR RAW",
241 FileType::Mrw => "Minolta MRW RAW",
242 FileType::Erf => "Epson ERF RAW",
243 FileType::Fff => "Hasselblad FFF RAW",
244 FileType::Iiq => "Phase One IIQ RAW",
245 FileType::Rwl => "Leica RWL RAW",
246 FileType::Mef => "Mamiya MEF RAW",
247 FileType::Srw => "Samsung SRW RAW",
248 FileType::X3f => "Sigma X3F RAW",
249 FileType::Gpr => "GoPro GPR RAW",
250 FileType::Arq => "Sony ARQ RAW",
251 FileType::ThreeFR => "Hasselblad 3FR RAW",
252 FileType::Crm => "Canon Cinema RAW",
253 FileType::Mp4 => "MP4 video",
255 FileType::QuickTime => "QuickTime video",
256 FileType::Avi => "AVI",
257 FileType::Mkv => "Matroska video",
258 FileType::WebM => "WebM video",
259 FileType::Wmv => "Windows Media Video",
260 FileType::Asf => "Advanced Systems Format",
261 FileType::Flv => "Flash Video",
262 FileType::Mxf => "Material Exchange Format",
263 FileType::Czi => "CZI",
264 FileType::M2ts => "MPEG-2 Transport Stream",
265 FileType::Mpeg => "MPEG video",
266 FileType::ThreeGP => "3GPP multimedia",
267 FileType::RealMedia => "RealMedia",
268 FileType::R3d => "Redcode RAW video",
269 FileType::Dvb => "Digital Video Broadcasting",
270 FileType::Lrv => "GoPro Low-Res Video",
271 FileType::Mqv => "Sony Movie",
272 FileType::F4v => "Adobe Flash Video",
273 FileType::Wtv => "Windows Recorded TV",
274 FileType::DvrMs => "Microsoft DVR",
275 FileType::Mp3 => "MP3 audio",
277 FileType::Flac => "FLAC audio",
278 FileType::Ogg => "Ogg Vorbis audio",
279 FileType::Wav => "WAV audio",
280 FileType::Aiff => "AIFF",
281 FileType::Aac => "AAC audio",
282 FileType::Opus => "Opus audio",
283 FileType::Mpc => "Musepack audio",
284 FileType::Ape => "Monkey's Audio",
285 FileType::WavPack => "WavPack audio",
286 FileType::Ofr => "OptimFROG audio",
287 FileType::Dsf => "DSD Stream File",
288 FileType::Audible => "Audible audiobook",
289 FileType::RealAudio => "RealAudio",
290 FileType::Wma => "Windows Media Audio",
291 FileType::M4a => "MPEG-4 Audio",
292 FileType::Dss => "DSS",
293 FileType::Pdf => "PDF document",
295 FileType::PostScript => "PostScript",
296 FileType::Doc => "Microsoft Word (legacy)",
297 FileType::Docx => "Microsoft Word",
298 FileType::Xls => "Microsoft Excel (legacy)",
299 FileType::Xlsx => "Microsoft Excel",
300 FileType::Ppt => "Microsoft PowerPoint (legacy)",
301 FileType::Pptx => "Microsoft PowerPoint",
302 FileType::Numbers => "Apple Numbers",
303 FileType::Pages => "Apple Pages",
304 FileType::Key => "Apple Keynote",
305 FileType::InDesign => "Adobe InDesign",
306 FileType::Rtf => "Rich Text Format",
307 FileType::Zip => "ZIP archive",
309 FileType::Rar => "RAR archive",
310 FileType::SevenZ => "7-Zip archive",
311 FileType::Gzip => "GZIP",
312 FileType::Xmp => "XMP sidecar",
314 FileType::Mie => "MIE metadata",
315 FileType::Exv => "Exiv2 metadata",
316 FileType::Vrd => "VRD",
317 FileType::Dr4 => "DR4",
318 FileType::Icc => "ICC color profile",
319 FileType::Html => "HTML document",
320 FileType::Exe => "Windows executable",
321 FileType::Font => "Font file",
322 FileType::Swf => "Shockwave Flash",
323 FileType::Dicom => "DICOM medical image",
324 FileType::Fits => "FITS astronomical image",
325 FileType::Mrc => "MRC image",
326 FileType::Moi => "MOI",
327 FileType::MacOs => "MacOS",
328 FileType::Json => "JSON",
329 FileType::Pcap => "PCAP",
330 FileType::Pcapng => "PCAPNG",
331 FileType::Svg => "SVG",
332 FileType::Pgf => "PGF",
333 FileType::Xisf => "XISF",
334 FileType::Torrent => "Torrent",
335 FileType::Mobi => "MOBI",
336 FileType::SonyPmp => "PMP",
337 FileType::Plist => "PLIST",
338 FileType::Aae => "AAE",
339 FileType::KyoceraRaw => "KyoceraRaw",
340 FileType::PortableFloatMap => "PFM",
341 FileType::Fpf => "FPF",
342 FileType::Ods => "ODS",
343 FileType::Odt => "ODT",
344 FileType::Odp => "ODP",
345 FileType::Odg => "ODG",
346 FileType::Odf => "ODF",
347 FileType::Odb => "ODB",
348 FileType::Odi => "ODI",
349 FileType::Odc => "ODC",
350 FileType::Eip => "EIP",
351 }
352 }
353
354 pub fn mime_type(self) -> &'static str {
356 match self {
357 FileType::Jpeg => "image/jpeg",
358 FileType::Tiff | FileType::Btf => "image/tiff",
359 FileType::Png => "image/png",
360 FileType::Gif => "image/gif",
361 FileType::Bmp => "image/bmp",
362 FileType::WebP => "image/webp",
363 FileType::Heif => "image/heif",
364 FileType::Avif => "image/avif",
365 FileType::Psd => "image/vnd.adobe.photoshop",
366 FileType::Jp2 => "image/jp2",
367 FileType::J2c => "image/x-j2c",
368 FileType::Jxl => "image/jxl",
369 FileType::Jxr => "image/jxr",
370 FileType::Flif => "image/flif",
371 FileType::Bpg => "image/bpg",
372 FileType::Exr => "image/x-exr",
373 FileType::Ico => "image/x-icon",
374 FileType::Jps => "image/x-jps",
375 FileType::DjVu => "image/vnd.djvu",
376 FileType::Xcf => "image/x-xcf",
377 FileType::Pcx => "image/x-pcx",
378 FileType::Pict => "image/x-pict",
379 FileType::Psp => "image/x-psp",
380 FileType::Hdr => "image/vnd.radiance",
381 FileType::Rwz => "image/x-rawzor",
382 FileType::Mng => "video/x-mng",
383 FileType::PhotoCd => "image/x-photo-cd",
384 FileType::Cr2 => "image/x-canon-cr2",
386 FileType::Cr3 | FileType::Crm => "image/x-canon-cr3",
387 FileType::Crw => "image/x-canon-crw",
388 FileType::Nef => "image/x-nikon-nef",
389 FileType::Arw | FileType::Arq => "image/x-sony-arw",
390 FileType::Sr2 => "image/x-sony-sr2",
391 FileType::Srf => "image/x-sony-srf",
392 FileType::Orf => "image/x-olympus-orf",
393 FileType::Rw2 => "image/x-panasonic-rw2",
394 FileType::Dng | FileType::Gpr => "image/x-adobe-dng",
395 FileType::Raf => "image/x-fuji-raf",
396 FileType::Pef => "image/x-pentax-pef",
397 FileType::Dcr => "image/x-kodak-dcr",
398 FileType::Mrw => "image/x-minolta-mrw",
399 FileType::Erf => "image/x-epson-erf",
400 FileType::Fff | FileType::ThreeFR => "image/x-hasselblad-fff",
401 FileType::Iiq => "image/x-phaseone-iiq",
402 FileType::Rwl => "image/x-leica-rwl",
403 FileType::Mef => "image/x-mamiya-mef",
404 FileType::Srw => "image/x-samsung-srw",
405 FileType::X3f => "image/x-sigma-x3f",
406 FileType::Mp4 | FileType::F4v => "video/mp4",
408 FileType::QuickTime | FileType::Mqv => "video/quicktime",
409 FileType::Avi => "video/x-msvideo",
410 FileType::Mkv => "video/x-matroska",
411 FileType::WebM => "video/webm",
412 FileType::Wmv => "video/x-ms-wmv",
413 FileType::Asf => "video/x-ms-asf",
414 FileType::Flv => "video/x-flv",
415 FileType::Mxf => "application/mxf",
416 FileType::Czi => "image/czi",
417 FileType::M2ts => "video/mp2t",
418 FileType::Mpeg => "video/mpeg",
419 FileType::ThreeGP => "video/3gpp",
420 FileType::RealMedia => "application/vnd.rn-realmedia",
421 FileType::R3d => "video/x-red-r3d",
422 FileType::Dvb => "video/dvb",
423 FileType::Lrv => "video/mp4",
424 FileType::Wtv => "video/x-ms-wtv",
425 FileType::DvrMs => "video/x-ms-dvr",
426 FileType::Mp3 => "audio/mpeg",
428 FileType::Flac => "audio/flac",
429 FileType::Ogg | FileType::Opus => "audio/ogg",
430 FileType::Wav => "audio/wav",
431 FileType::Aiff => "audio/x-aiff",
432 FileType::Aac => "audio/aac",
433 FileType::Mpc => "audio/x-musepack",
434 FileType::Ape => "audio/x-ape",
435 FileType::WavPack => "audio/x-wavpack",
436 FileType::Ofr => "audio/x-ofr",
437 FileType::Dsf => "audio/dsf",
438 FileType::Audible => "audio/x-pn-audibleaudio",
439 FileType::RealAudio => "audio/x-pn-realaudio",
440 FileType::Wma => "audio/x-ms-wma",
441 FileType::M4a => "audio/mp4",
442 FileType::Dss => "audio/x-dss",
443 FileType::Pdf => "application/pdf",
445 FileType::PostScript => "application/postscript",
446 FileType::Doc => "application/msword",
447 FileType::Docx => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
448 FileType::Xls => "application/vnd.ms-excel",
449 FileType::Xlsx => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
450 FileType::Ppt => "application/vnd.ms-powerpoint",
451 FileType::Pptx => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
452 FileType::Numbers => "application/x-iwork-numbers-sffnumbers",
453 FileType::Pages => "application/x-iwork-pages-sffpages",
454 FileType::Key => "application/x-iwork-keynote-sffkey",
455 FileType::InDesign => "application/x-indesign",
456 FileType::Rtf => "application/rtf",
457 FileType::Zip => "application/zip",
459 FileType::Rar => "application/x-rar-compressed",
460 FileType::SevenZ => "application/x-7z-compressed",
461 FileType::Gzip => "application/x-gzip",
462 FileType::Xmp => "application/rdf+xml",
464 FileType::Mie => "application/x-mie",
465 FileType::Exv => "application/x-exv",
466 FileType::Vrd => "application/octet-stream",
467 FileType::Dr4 => "application/octet-stream",
468 FileType::Icc => "application/vnd.icc.profile",
469 FileType::Html => "text/html",
470 FileType::Exe => "application/x-dosexec",
471 FileType::Font => "font/sfnt",
472 FileType::Swf => "application/x-shockwave-flash",
473 FileType::Dicom => "application/dicom",
474 FileType::Fits => "application/fits",
475 FileType::Mrc => "image/x-mrc",
476 FileType::Moi => "application/octet-stream",
477 FileType::MacOs => "application/unknown",
478 FileType::Json => "application/json",
479 FileType::Pcap => "application/vnd.tcpdump.pcap",
480 FileType::Pcapng => "application/vnd.tcpdump.pcap",
481 FileType::Svg => "image/svg+xml",
482 FileType::Pgf => "image/pgf",
483 FileType::Xisf => "application/xisf",
484 FileType::Torrent => "application/x-bittorrent",
485 FileType::Mobi => "application/x-mobipocket-ebook",
486 FileType::SonyPmp => "image/x-sony-pmp",
487 FileType::Plist => "application/x-plist",
488 FileType::Aae => "application/vnd.apple.photos",
489 FileType::KyoceraRaw => "image/x-raw",
490 FileType::Lfp => "image/x-lytro-lfp",
491 FileType::PortableFloatMap => "image/x-pfm",
492 FileType::Fpf => "image/x-flir-fpf",
493 FileType::Ods => "application/vnd.oasis.opendocument.spreadsheet",
494 FileType::Odt => "application/vnd.oasis.opendocument.text",
495 FileType::Odp => "application/vnd.oasis.opendocument.presentation",
496 FileType::Odg => "application/vnd.oasis.opendocument.graphics",
497 FileType::Odf => "application/vnd.oasis.opendocument.formula",
498 FileType::Odb => "application/vnd.oasis.opendocument.database",
499 FileType::Odi => "application/vnd.oasis.opendocument.image",
500 FileType::Odc => "application/vnd.oasis.opendocument.chart",
501 FileType::Eip => "application/x-captureone",
502 }
503 }
504
505 pub fn extensions(self) -> &'static [&'static str] {
507 match self {
508 FileType::Jpeg => &["jpg", "jpeg", "jpe", "jif", "jfif"],
509 FileType::Tiff => &["tif", "tiff"],
510 FileType::Png => &["png"],
511 FileType::Gif => &["gif"],
512 FileType::Bmp => &["bmp", "dib"],
513 FileType::WebP => &["webp"],
514 FileType::Heif => &["heif", "heic", "hif"],
515 FileType::Avif => &["avif"],
516 FileType::Psd => &["psd", "psb", "psdt"],
517 FileType::Jp2 => &["jp2", "jpf", "jpm", "jpx", "jph"],
518 FileType::J2c => &["j2c", "j2k", "jpc"],
519 FileType::Jxl => &["jxl"],
520 FileType::Jxr => &["jxr", "hdp", "wdp"],
521 FileType::Flif => &["flif"],
522 FileType::Bpg => &["bpg"],
523 FileType::Exr => &["exr"],
524 FileType::Ico => &["ico", "cur"],
525 FileType::Jps => &["jps"],
526 FileType::DjVu => &["djvu", "djv"],
527 FileType::Xcf => &["xcf"],
528 FileType::Pcx => &["pcx"],
529 FileType::Pict => &["pict", "pct"],
530 FileType::Psp => &["psp", "pspimage"],
531 FileType::Hdr => &["hdr"],
532 FileType::Rwz => &["rwz"],
533 FileType::Btf => &["btf"],
534 FileType::Mng => &["mng", "jng"],
535 FileType::PhotoCd => &["pcd"],
536 FileType::Cr2 => &["cr2"],
538 FileType::Cr3 => &["cr3"],
539 FileType::Crw => &["crw", "ciff"],
540 FileType::Nef => &["nef", "nrw"],
541 FileType::Arw => &["arw"],
542 FileType::Sr2 => &["sr2"],
543 FileType::Srf => &["srf"],
544 FileType::Orf => &["orf", "ori"],
545 FileType::Rw2 => &["rw2"],
546 FileType::Dng => &["dng"],
547 FileType::Raf => &["raf"],
548 FileType::Pef => &["pef"],
549 FileType::Dcr => &["dcr"],
550 FileType::Mrw => &["mrw"],
551 FileType::Erf => &["erf"],
552 FileType::Fff => &["fff"],
553 FileType::Iiq => &["iiq"],
554 FileType::Rwl => &["rwl"],
555 FileType::Mef => &["mef"],
556 FileType::Srw => &["srw"],
557 FileType::X3f => &["x3f"],
558 FileType::Gpr => &["gpr"],
559 FileType::Arq => &["arq"],
560 FileType::ThreeFR => &["3fr"],
561 FileType::Crm => &["crm"],
562 FileType::Mp4 => &["mp4", "m4v"],
564 FileType::QuickTime => &["mov", "qt"],
565 FileType::Avi => &["avi"],
566 FileType::Mkv => &["mkv", "mks"],
567 FileType::WebM => &["webm"],
568 FileType::Wmv => &["wmv"],
569 FileType::Asf => &["asf"],
570 FileType::Flv => &["flv"],
571 FileType::Mxf => &["mxf"],
572 FileType::Czi => &["czi"],
573 FileType::M2ts => &["m2ts", "mts", "m2t", "ts"],
574 FileType::Mpeg => &["mpg", "mpeg", "m2v", "mpv"],
575 FileType::ThreeGP => &["3gp", "3gpp", "3g2", "3gp2"],
576 FileType::RealMedia => &["rm", "rv", "rmvb"],
577 FileType::R3d => &["r3d"],
578 FileType::Dvb => &["dvb"],
579 FileType::Lrv => &["lrv", "lrf"],
580 FileType::Mqv => &["mqv"],
581 FileType::F4v => &["f4v", "f4a", "f4b", "f4p"],
582 FileType::Wtv => &["wtv"],
583 FileType::DvrMs => &["dvr-ms"],
584 FileType::Mp3 => &["mp3"],
586 FileType::Flac => &["flac"],
587 FileType::Ogg => &["ogg", "oga", "ogv"],
588 FileType::Wav => &["wav"],
589 FileType::Aiff => &["aiff", "aif", "aifc"],
590 FileType::Aac => &["aac"],
591 FileType::Opus => &["opus"],
592 FileType::Mpc => &["mpc"],
593 FileType::Ape => &["ape"],
594 FileType::WavPack => &["wv", "wvp"],
595 FileType::Ofr => &["ofr"],
596 FileType::Dsf => &["dsf"],
597 FileType::Audible => &["aa", "aax"],
598 FileType::RealAudio => &["ra"],
599 FileType::Wma => &["wma"],
600 FileType::M4a => &["m4a", "m4b", "m4p"],
601 FileType::Dss => &["dss"],
602 FileType::Pdf => &["pdf"],
604 FileType::PostScript => &["ps", "eps", "epsf"],
605 FileType::Doc => &["doc", "dot"],
606 FileType::Docx => &["docx", "docm"],
607 FileType::Xls => &["xls", "xlt"],
608 FileType::Xlsx => &["xlsx", "xlsm", "xlsb"],
609 FileType::Ppt => &["ppt", "pps", "pot"],
610 FileType::Pptx => &["pptx", "pptm"],
611 FileType::Numbers => &["numbers", "nmbtemplate"],
612 FileType::Pages => &["pages"],
613 FileType::Key => &["key", "kth"],
614 FileType::InDesign => &["ind", "indd", "indt"],
615 FileType::Rtf => &["rtf"],
616 FileType::Zip => &["zip"],
618 FileType::Rar => &["rar"],
619 FileType::SevenZ => &["7z"],
620 FileType::Gzip => &["gz", "gzip"],
621 FileType::Xmp => &["xmp", "inx", "xml"],
623 FileType::Mie => &["mie"],
624 FileType::Exv => &["exv"],
625 FileType::Vrd => &["vrd"],
626 FileType::Dr4 => &["dr4"],
627 FileType::Icc => &["icc", "icm"],
628 FileType::Html => &["html", "htm", "xhtml", "svg"],
629 FileType::Exe => &["exe", "dll", "elf", "so", "dylib", "a", "macho", "o"],
630 FileType::Font => &["ttf", "otf", "woff", "woff2", "ttc", "dfont", "afm", "pfa", "pfb"],
631 FileType::Swf => &["swf"],
632 FileType::Dicom => &["dcm"],
633 FileType::Fits => &["fits", "fit", "fts"],
634 FileType::Mrc => &["mrc"],
635 FileType::Moi => &["moi"],
636 FileType::MacOs => &["macos"],
637 FileType::Json => &["json"],
638 FileType::Pcap => &["pcap", "cap"],
639 FileType::Pcapng => &["pcapng", "ntar"],
640 FileType::Svg => &["svg"],
641 FileType::Pgf => &["pgf"],
642 FileType::Xisf => &["xisf"],
643 FileType::Torrent => &["torrent"],
644 FileType::Mobi => &["mobi", "azw", "azw3"],
645 FileType::SonyPmp => &["pmp"],
646 FileType::Plist => &["plist"],
647 FileType::Aae => &["aae"],
648 FileType::KyoceraRaw => &["raw"],
649 FileType::PortableFloatMap => &["pfm"],
650 FileType::Fpf => &["fpf"],
651 FileType::Ods => &["ods"],
652 FileType::Odt => &["odt"],
653 FileType::Odp => &["odp"],
654 FileType::Odg => &["odg"],
655 FileType::Odf => &["odf"],
656 FileType::Odb => &["odb"],
657 FileType::Odi => &["odi"],
658 FileType::Odc => &["odc"],
659 FileType::Lfp => &["lfp", "lfr"],
660 FileType::Eip => &["eip"],
661 }
662 }
663
664 pub fn support(self) -> Support {
666 match self {
667 FileType::Xmp | FileType::Mie | FileType::Exv => Support::ReadWriteCreate,
669 FileType::Jpeg
671 | FileType::Tiff
672 | FileType::Png
673 | FileType::Gif
674 | FileType::WebP
675 | FileType::Heif
676 | FileType::Avif
677 | FileType::Psd
678 | FileType::Jp2
679 | FileType::Jxl
680 | FileType::Jxr
681 | FileType::Flif
682 | FileType::Cr2
683 | FileType::Cr3
684 | FileType::Crw
685 | FileType::Nef
686 | FileType::Arw
687 | FileType::Arq
688 | FileType::Sr2
689 | FileType::Orf
690 | FileType::Rw2
691 | FileType::Dng
692 | FileType::Raf
693 | FileType::Pef
694 | FileType::Erf
695 | FileType::Fff
696 | FileType::Iiq
697 | FileType::Rwl
698 | FileType::Mef
699 | FileType::Srw
700 | FileType::X3f
701 | FileType::Gpr
702 | FileType::Crm
703 | FileType::Mp4
704 | FileType::QuickTime
705 | FileType::ThreeGP
706 | FileType::Dvb
707 | FileType::Lrv
708 | FileType::Mqv
709 | FileType::F4v
710 | FileType::Pdf
711 | FileType::PostScript
712 | FileType::InDesign
713 | FileType::Vrd
714 | FileType::Dr4
715 | FileType::Audible => Support::ReadWrite,
716 _ => Support::Read,
718 }
719 }
720
721 pub fn all() -> &'static [FileType] {
723 ALL_FILE_TYPES
724 }
725}
726
727static ALL_FILE_TYPES: &[FileType] = &[
728 FileType::Jpeg, FileType::Tiff, FileType::Png, FileType::Gif, FileType::Bmp,
730 FileType::WebP, FileType::Heif, FileType::Avif, FileType::Psd, FileType::Jp2,
731 FileType::J2c, FileType::Jxl, FileType::Jxr, FileType::Flif, FileType::Bpg,
732 FileType::Exr, FileType::Ico, FileType::Jps,
733 FileType::DjVu, FileType::Xcf, FileType::Pcx, FileType::Pict, FileType::Psp,
735 FileType::Hdr, FileType::Rwz, FileType::Btf, FileType::Mng, FileType::PhotoCd,
736 FileType::Cr2, FileType::Cr3, FileType::Crw, FileType::Nef, FileType::Arw,
738 FileType::Sr2, FileType::Srf, FileType::Orf, FileType::Rw2, FileType::Dng,
739 FileType::Raf, FileType::Pef, FileType::Dcr, FileType::Mrw, FileType::Erf,
740 FileType::Fff, FileType::Iiq, FileType::Rwl, FileType::Mef, FileType::Srw,
741 FileType::X3f, FileType::Gpr, FileType::Arq, FileType::ThreeFR, FileType::Crm,
742 FileType::Mp4, FileType::QuickTime, FileType::Avi, FileType::Mkv, FileType::WebM,
744 FileType::Wmv, FileType::Asf, FileType::Flv, FileType::Mxf, FileType::M2ts,
745 FileType::Mpeg, FileType::ThreeGP, FileType::RealMedia, FileType::R3d,
746 FileType::Dvb, FileType::Lrv, FileType::Mqv, FileType::F4v, FileType::Wtv,
747 FileType::DvrMs,
748 FileType::Mp3, FileType::Flac, FileType::Ogg, FileType::Wav, FileType::Aiff,
750 FileType::Aac, FileType::Opus, FileType::Mpc, FileType::Ape, FileType::WavPack,
751 FileType::Ofr, FileType::Dsf, FileType::Audible, FileType::RealAudio,
752 FileType::Wma, FileType::M4a, FileType::Dss,
753 FileType::Pdf, FileType::PostScript, FileType::Doc, FileType::Docx,
755 FileType::Xls, FileType::Xlsx, FileType::Ppt, FileType::Pptx,
756 FileType::Numbers, FileType::Pages, FileType::Key,
757 FileType::InDesign, FileType::Rtf,
758 FileType::Zip, FileType::Rar, FileType::SevenZ, FileType::Gzip,
760 FileType::Xmp, FileType::Mie, FileType::Exv, FileType::Vrd, FileType::Dr4, FileType::Icc,
762 FileType::Html, FileType::Exe, FileType::Font, FileType::Swf,
763 FileType::Dicom, FileType::Fits,
764 FileType::Mrc, FileType::Moi, FileType::MacOs, FileType::Json,
765 FileType::Pcap, FileType::Pcapng,
766 FileType::Svg,
767 FileType::Pgf, FileType::Xisf, FileType::Torrent, FileType::Mobi, FileType::SonyPmp,
768 FileType::Plist, FileType::Aae, FileType::KyoceraRaw,
769 FileType::PortableFloatMap,
770 FileType::Fpf,
771 FileType::Lfp,
772 FileType::Ods, FileType::Odt, FileType::Odp, FileType::Odg,
774 FileType::Odf, FileType::Odb, FileType::Odi, FileType::Odc,
775 FileType::Eip,
777];
778
779pub fn detect_from_magic(header: &[u8]) -> Option<FileType> {
781 if header.len() < 4 {
782 return None;
783 }
784
785 if header.starts_with(&[0xFF, 0xD8, 0xFF]) {
789 return Some(FileType::Jpeg);
790 }
791
792 if header.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
794 return Some(FileType::Png);
795 }
796
797 if header.starts_with(&[0x89, 0x4C, 0x46, 0x50, 0x0D, 0x0A, 0x1A, 0x0A]) {
799 return Some(FileType::Lfp);
800 }
801
802 if header.starts_with(b"GIF8") && header.len() >= 6 && (header[4] == b'7' || header[4] == b'9') {
804 return Some(FileType::Gif);
805 }
806
807 if header.len() >= 8 && header.starts_with(b"IIII")
809 && (header[4] == 0x04 || header[4] == 0x05) && header[5] == 0x00
810 && header[6] == 0x04 && header[7] == 0x00
811 {
812 return Some(FileType::Dr4);
813 }
814
815 if header.starts_with(b"CANON OPTIONAL DATA\0") {
817 return Some(FileType::Vrd);
818 }
819
820 if header.len() >= 4 {
822 let is_le = header[0] == b'I' && header[1] == b'I' && header[2] == 0x2A && header[3] == 0x00;
823 let is_be = header[0] == b'M' && header[1] == b'M' && header[2] == 0x00 && header[3] == 0x2A;
824 if is_le || is_be {
825 if header.len() >= 10 && is_le && header[8] == b'C' && header[9] == b'R' {
827 return Some(FileType::Cr2);
828 }
829 if header.len() >= 12 && is_le && &header[8..12] == b"IIII" {
831 return Some(FileType::Iiq);
832 }
833 if header.len() >= 12 && is_be && &header[8..12] == b"MMMM" {
834 return Some(FileType::Iiq);
835 }
836 if header.len() >= 4 && is_le && header[0] == b'I' && header[1] == b'I' {
838 if header.len() >= 8 {
839 }
842 }
843 return Some(FileType::Tiff);
846 }
847 let is_btf_le = header[0] == b'I' && header[1] == b'I' && header[2] == 0x2B && header[3] == 0x00;
849 let is_btf_be = header[0] == b'M' && header[1] == b'M' && header[2] == 0x00 && header[3] == 0x2B;
850 if is_btf_le || is_btf_be {
851 return Some(FileType::Btf);
852 }
853 }
854
855 if header.starts_with(b"BM") && header.len() >= 6 {
857 return Some(FileType::Bmp);
858 }
859
860 if header.len() >= 12 && header.starts_with(b"RIFF") {
862 match &header[8..12] {
863 b"WEBP" => return Some(FileType::WebP),
864 b"AVI " => return Some(FileType::Avi),
865 b"WAVE" => return Some(FileType::Wav),
866 _ => {}
867 }
868 }
869
870 if header.starts_with(b"8BPS") {
872 return Some(FileType::Psd);
873 }
874
875 if header.len() >= 12 && header.starts_with(&[0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20]) {
877 return Some(FileType::Jp2);
878 }
879
880 if header.starts_with(&[0xFF, 0x4F, 0xFF, 0x51]) {
882 return Some(FileType::J2c);
883 }
884
885 if header.len() >= 2 && header[0] == 0xFF && header[1] == 0x0A {
887 return Some(FileType::Jxl);
888 }
889 if header.len() >= 12 && header.starts_with(&[0x00, 0x00, 0x00, 0x0C, 0x4A, 0x58, 0x4C, 0x20]) {
890 return Some(FileType::Jxl);
891 }
892
893 if header.starts_with(b"FLIF") {
895 return Some(FileType::Flif);
896 }
897
898 if header.starts_with(&[0x42, 0x50, 0x47, 0xFB]) {
900 return Some(FileType::Bpg);
901 }
902
903 if header.starts_with(&[0x76, 0x2F, 0x31, 0x01]) {
905 return Some(FileType::Exr);
906 }
907
908 if header.len() >= 4 && header[0] == 0 && header[1] == 0 && (header[2] == 1 || header[2] == 2) && header[3] == 0 {
910 return Some(FileType::Ico);
911 }
912
913 if header.len() >= 8 && header.starts_with(b"AT&TFORM") {
915 return Some(FileType::DjVu);
916 }
917
918 if header.starts_with(b"gimp xcf") {
920 return Some(FileType::Xcf);
921 }
922
923 if header.starts_with(&[0x8A, 0x4D, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
925 return Some(FileType::Mng);
926 }
927
928 if header.starts_with(&[0x8B, 0x4A, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
930 return Some(FileType::Mng);
931 }
932
933 if header.len() >= 10 && header.starts_with(b"#?RADIANCE") {
935 return Some(FileType::Hdr);
936 }
937
938 if header.len() >= 3 && header[0] == b'P' && (header[1] == b'F' || header[1] == b'f') && header[2] == b'\n' {
940 return Some(FileType::PortableFloatMap);
941 }
942
943 if header.starts_with(b"FPF Public Image Format\0") {
945 return Some(FileType::Fpf);
946 }
947
948 if header.len() >= 15 && header.starts_with(b"FUJIFILMCCD-RAW") {
952 return Some(FileType::Raf);
953 }
954
955 if header.len() >= 14 && header[0] == b'I' && header[1] == b'I'
957 && header[2] == 0x1A && header[3] == 0x00
958 && &header[6..14] == b"HEAPCCDR"
959 {
960 return Some(FileType::Crw);
961 }
962
963 if header.starts_with(&[0x00, 0x4D, 0x52, 0x4D]) {
965 return Some(FileType::Mrw);
966 }
967
968 if header.starts_with(b"FOVb") {
970 return Some(FileType::X3f);
971 }
972
973 if header.len() >= 4 && header[0] == b'I' && header[1] == b'I' && header[2] == 0x55 && header[3] == 0x00 {
975 return Some(FileType::Rw2);
976 }
977
978 if header.len() >= 12 && &header[4..8] == b"ftyp" {
982 let brand = &header[8..12];
983 if brand == b"heic" || brand == b"mif1" || brand == b"heim" || brand == b"heis"
985 || brand == b"msf1"
986 {
987 return Some(FileType::Heif);
988 }
989 if brand == b"avif" || brand == b"avis" {
991 return Some(FileType::Avif);
992 }
993 if brand == b"crx " {
995 return Some(FileType::Cr3);
996 }
997 if brand == b"qt " {
999 return Some(FileType::QuickTime);
1000 }
1001 if brand == b"3gp4" || brand == b"3gp5" || brand == b"3gp6" || brand == b"3g2a" {
1003 return Some(FileType::ThreeGP);
1004 }
1005 if brand == b"M4A " || brand == b"M4B " || brand == b"M4P " {
1007 return Some(FileType::M4a);
1008 }
1009 if brand == b"M4V " || brand == b"M4VH" || brand == b"M4VP" {
1010 return Some(FileType::Mp4);
1011 }
1012 if brand == b"F4V " || brand == b"F4P " {
1014 return Some(FileType::F4v);
1015 }
1016 return Some(FileType::Mp4);
1018 }
1019
1020 if header.len() >= 8 {
1022 let atom_type = &header[4..8];
1023 if atom_type == b"moov" || atom_type == b"mdat" || atom_type == b"wide"
1024 || atom_type == b"free" || atom_type == b"pnot" || atom_type == b"skip"
1025 {
1026 return Some(FileType::QuickTime);
1027 }
1028 }
1029
1030 if header.starts_with(&[0x1A, 0x45, 0xDF, 0xA3]) {
1032 return Some(FileType::Mkv);
1033 }
1035
1036 if header.starts_with(b"FLV\x01") {
1038 return Some(FileType::Flv);
1039 }
1040
1041 if header.len() >= 16 && header.starts_with(&[0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11]) {
1043 return Some(FileType::Asf);
1044 }
1046
1047 if header.len() >= 8 && header.starts_with(&[0x06, 0x0E, 0x2B, 0x34]) {
1049 return Some(FileType::Mxf);
1050 }
1051
1052 if header.len() >= 10 && header.starts_with(b"ZISRAWFILE") {
1054 return Some(FileType::Czi);
1055 }
1056
1057 if header.len() >= 40 && &header[36..40] == b"acsp" {
1059 return Some(FileType::Icc);
1060 }
1061
1062 if header.len() >= 4 && header[0] == 0 && header[1] == 0 && header[2] == 1
1064 && (header[3] == 0xBA || header[3] == 0xBB || (header[3] & 0xF0) == 0xE0)
1065 {
1066 return Some(FileType::Mpeg);
1067 }
1068
1069 if header.len() >= 1 && header[0] == 0x47 {
1071 if header.len() >= 376 && header[188] == 0x47 {
1072 return Some(FileType::M2ts);
1073 }
1074 if header.len() >= 384 && header[192] == 0x47 {
1075 return Some(FileType::M2ts);
1076 }
1077 }
1078
1079 if header.starts_with(b".RMF") {
1081 return Some(FileType::RealMedia);
1082 }
1083
1084 if header.starts_with(b"RED1") || header.starts_with(b"RED2") {
1086 return Some(FileType::R3d);
1087 }
1088
1089 if header.starts_with(b"ID3") {
1093 return Some(FileType::Mp3);
1094 }
1095 if header.len() >= 2 && header[0] == 0xFF
1099 && (header[1] == 0xF0 || header[1] == 0xF1
1100 || header[1] == 0xF8 || header[1] == 0xF9)
1101 {
1102 return Some(FileType::Aac);
1103 }
1104 if header.len() >= 2 && header[0] == 0xFF && (header[1] & 0xE0) == 0xE0 {
1106 return Some(FileType::Mp3);
1107 }
1108
1109 if header.starts_with(b"fLaC") {
1111 return Some(FileType::Flac);
1112 }
1113
1114 if header.starts_with(b"OggS") {
1116 return Some(FileType::Ogg);
1117 }
1118
1119 if header.len() >= 12 && header.starts_with(b"FORM") {
1121 if &header[8..12] == b"AIFF" || &header[8..12] == b"AIFC" {
1122 return Some(FileType::Aiff);
1123 }
1124 }
1125
1126 if header.starts_with(b"MAC ") {
1128 return Some(FileType::Ape);
1129 }
1130
1131 if header.len() >= 0x20 && &header[0x19..0x20] == b"ARECOYK" {
1133 return Some(FileType::KyoceraRaw);
1134 }
1135
1136 if header.starts_with(b"MP+") || header.starts_with(b"MPCK") {
1138 return Some(FileType::Mpc);
1139 }
1140
1141 if header.starts_with(b"wvpk") {
1143 return Some(FileType::WavPack);
1144 }
1145
1146 if header.starts_with(b"DSD ") {
1148 return Some(FileType::Dsf);
1149 }
1150
1151 if header.starts_with(b"OFR ") {
1153 return Some(FileType::Ofr);
1154 }
1155
1156 if header.len() >= 4 && header[0] == b'.' && header[1] == b'r' && header[2] == b'a' && header[3] == 0xFD {
1158 return Some(FileType::RealAudio);
1159 }
1160
1161 if header.len() >= 4
1163 && (header[0] == 0x02 || header[0] == 0x03)
1164 && (header[1] == b'd')
1165 && (header[2] == b's')
1166 && (header[3] == b's' || header[3] == b'2')
1167 {
1168 return Some(FileType::Dss);
1169 }
1170
1171 if header.starts_with(b"%PDF-") {
1175 return Some(FileType::Pdf);
1176 }
1177
1178 if header.starts_with(b"%!PS") || header.starts_with(b"%!Adobe") {
1180 return Some(FileType::PostScript);
1181 }
1182
1183 if header.starts_with(&[0xD0, 0xCF, 0x11, 0xE0]) {
1185 return Some(FileType::Doc); }
1187
1188 if header.starts_with(b"{\\rtf") {
1190 return Some(FileType::Rtf);
1191 }
1192
1193 if header.len() >= 8 && header.starts_with(&[0x06, 0x06, 0xED, 0xF5, 0xD8, 0x1D, 0x46, 0xE5]) {
1195 return Some(FileType::InDesign);
1196 }
1197
1198 if header.starts_with(&[0x50, 0x4B, 0x03, 0x04]) {
1202 return Some(FileType::Zip);
1203 }
1205
1206 if header.len() >= 6 && header.starts_with(b"Rar!\x1A\x07") {
1208 return Some(FileType::Rar);
1209 }
1210
1211 if header.len() >= 6 && header.starts_with(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]) {
1213 return Some(FileType::SevenZ);
1214 }
1215
1216 if header.len() >= 2 && header[0] == 0x1F && header[1] == 0x8B {
1218 return Some(FileType::Gzip);
1219 }
1220
1221 if header.len() >= 4 && header[0] == 0x0A && header[1] == 0x0D && header[2] == 0x0D && header[3] == 0x0A {
1223 return Some(FileType::Pcapng);
1224 }
1225
1226 if header.len() >= 4 && (
1228 (header[0] == 0xD4 && header[1] == 0xC3 && header[2] == 0xB2 && header[3] == 0xA1) ||
1229 (header[0] == 0xA1 && header[1] == 0xB2 && header[2] == 0xC3 && header[3] == 0xD4)
1230 ) {
1231 return Some(FileType::Pcap);
1232 }
1233
1234 if header.len() >= 3
1238 && ((header[0] == b'F' || header[0] == b'C' || header[0] == b'Z')
1239 && header[1] == b'W'
1240 && header[2] == b'S')
1241 {
1242 return Some(FileType::Swf);
1243 }
1244
1245 if header.len() >= 132 && &header[128..132] == b"DICM" {
1247 return Some(FileType::Dicom);
1248 }
1249
1250
1251 if header.len() >= 214 {
1253 let ax1 = u32::from_le_bytes([header[64], header[65], header[66], header[67]]);
1254 let ax2 = u32::from_le_bytes([header[68], header[69], header[70], header[71]]);
1255 let ax3 = u32::from_le_bytes([header[72], header[73], header[74], header[75]]);
1256 if (1..=3).contains(&ax1) && (1..=3).contains(&ax2) && (1..=3).contains(&ax3)
1257 && &header[208..211] == b"MAP"
1258 {
1259 let ms0 = header[212];
1260 let ms1 = header[213];
1261 if (ms0 == 0x44 && ms1 == 0x44)
1262 || (ms0 == 0x44 && ms1 == 0x41)
1263 || (ms0 == 0x11 && ms1 == 0x11)
1264 {
1265 return Some(FileType::Mrc);
1266 }
1267 }
1268 }
1269
1270 if header.len() >= 9 && header.starts_with(b"SIMPLE =") {
1272 return Some(FileType::Fits);
1273 }
1274
1275 if header.len() >= 4 && header[0] == 0x7E && header[1] == 0x10 && header[2] == 0x04 {
1277 return Some(FileType::Mie);
1278 }
1279
1280 if header.starts_with(b"<?xpacket") || header.starts_with(b"<x:xmpmeta") {
1282 return Some(FileType::Xmp);
1283 }
1284
1285 if header.starts_with(b"<?xml") || header.starts_with(b"<svg") {
1287 let preview = &header[..header.len().min(512)];
1288 if preview.windows(4).any(|w| w == b"<svg") {
1289 return Some(FileType::Svg);
1290 }
1291 if preview.windows(5).any(|w| w == b"<html" || w == b"<HTML") {
1292 return Some(FileType::Html); }
1294 if preview.windows(10).any(|w| w == b"<x:xmpmeta") || preview.windows(9).any(|w| w == b"<?xpacket") {
1295 return Some(FileType::Xmp);
1296 }
1297 if preview.windows(4).any(|w| w == b"<rdf" || w == b"<RDF") {
1298 return Some(FileType::Xmp);
1299 }
1300 if preview.windows(7).any(|w| w == b"<plist") || preview.windows(20).any(|w| w == b"DTD PLIST") {
1302 return Some(FileType::Plist);
1303 }
1304 return Some(FileType::Xmp);
1306 }
1307
1308 if header.starts_with(b"<!DOCTYPE html") || header.starts_with(b"<!doctype html")
1310 || header.starts_with(b"<!DOCTYPE HTML") || header.starts_with(b"<html") || header.starts_with(b"<HTML")
1311 {
1312 return Some(FileType::Html);
1313 }
1314
1315 if header.starts_with(&[0x7F, b'E', b'L', b'F']) {
1317 return Some(FileType::Exe);
1318 }
1319
1320 if header.starts_with(&[0xFE, 0xED, 0xFA, 0xCE])
1322 || header.starts_with(&[0xCE, 0xFA, 0xED, 0xFE])
1323 {
1324 return Some(FileType::Exe);
1325 }
1326
1327 if header.starts_with(&[0xFE, 0xED, 0xFA, 0xCF])
1329 || header.starts_with(&[0xCF, 0xFA, 0xED, 0xFE])
1330 {
1331 return Some(FileType::Exe);
1332 }
1333
1334 if header.starts_with(&[0xCA, 0xFE, 0xBA, 0xBE]) {
1336 return Some(FileType::Exe);
1337 }
1338
1339 if header.starts_with(b"MZ") {
1341 return Some(FileType::Exe);
1342 }
1343
1344 if (header.starts_with(&[0x00, 0x01, 0x00, 0x00]) || header.starts_with(b"true") || header.starts_with(b"typ1"))
1346 && header.len() >= 12
1347 {
1348 return Some(FileType::Font);
1349 }
1350
1351 if header.starts_with(b"ttcf") {
1353 return Some(FileType::Font);
1354 }
1355
1356 if header.starts_with(b"OTTO") {
1358 return Some(FileType::Font);
1359 }
1360
1361 if header.starts_with(b"wOFF") {
1363 return Some(FileType::Font);
1364 }
1365
1366 if header.starts_with(b"wOF2") {
1368 return Some(FileType::Font);
1369 }
1370
1371 if header.len() >= 2 && header[0] == b'V' && header[1] == b'6' {
1373 return Some(FileType::Moi);
1374 }
1375
1376 if header.starts_with(b"PGF") {
1378 return Some(FileType::Pgf);
1379 }
1380
1381 if header.starts_with(b"XISF0100") {
1383 return Some(FileType::Xisf);
1384 }
1385
1386 if header.len() >= 27 && header.starts_with(b"Paint Shop Pro Image File\n\x1a") {
1388 return Some(FileType::Psp);
1389 }
1390
1391 if header.len() >= 12
1394 && header[8] == 0x00 && header[9] == 0x00
1395 && header[10] == 0x00 && header[11] == 0x7C
1396 {
1397 return Some(FileType::SonyPmp);
1398 }
1399
1400 None
1401}
1402
1403pub fn detect_from_extension(ext: &str) -> Option<FileType> {
1405 let ext_lower = ext.to_ascii_lowercase();
1406 let ext_lower = ext_lower.trim_start_matches('.');
1407
1408 for &ft in ALL_FILE_TYPES {
1409 for known_ext in ft.extensions() {
1410 if ext_lower == *known_ext {
1411 return Some(ft);
1412 }
1413 }
1414 }
1415
1416 None
1417}
1418
1419impl std::fmt::Display for FileType {
1420 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1421 write!(f, "{}", self.description())
1422 }
1423}
1424
1425impl std::fmt::Display for Support {
1426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1427 match self {
1428 Support::Read => write!(f, "R"),
1429 Support::ReadWrite => write!(f, "R/W"),
1430 Support::ReadWriteCreate => write!(f, "R/W/C"),
1431 }
1432 }
1433}
1434
1435#[cfg(test)]
1436mod tests {
1437 use super::*;
1438
1439 #[test]
1440 fn test_detect_jpeg() {
1441 let header = [0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, b'J', b'F', b'I', b'F'];
1442 assert_eq!(detect_from_magic(&header), Some(FileType::Jpeg));
1443 }
1444
1445 #[test]
1446 fn test_detect_png() {
1447 let header = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
1448 assert_eq!(detect_from_magic(&header), Some(FileType::Png));
1449 }
1450
1451 #[test]
1452 fn test_detect_tiff_le() {
1453 let header = [b'I', b'I', 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00];
1454 assert_eq!(detect_from_magic(&header), Some(FileType::Tiff));
1455 }
1456
1457 #[test]
1458 fn test_detect_tiff_be() {
1459 let header = [b'M', b'M', 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08];
1460 assert_eq!(detect_from_magic(&header), Some(FileType::Tiff));
1461 }
1462
1463 #[test]
1464 fn test_detect_cr2() {
1465 let header = [b'I', b'I', 0x2A, 0x00, 0x10, 0x00, 0x00, 0x00, b'C', b'R'];
1466 assert_eq!(detect_from_magic(&header), Some(FileType::Cr2));
1467 }
1468
1469 #[test]
1470 fn test_detect_pdf() {
1471 let header = b"%PDF-1.7 some more data here";
1472 assert_eq!(detect_from_magic(header), Some(FileType::Pdf));
1473 }
1474
1475 #[test]
1476 fn test_detect_webp() {
1477 let header = b"RIFF\x00\x00\x00\x00WEBP";
1478 assert_eq!(detect_from_magic(header), Some(FileType::WebP));
1479 }
1480
1481 #[test]
1482 fn test_detect_heif() {
1483 let mut header = [0u8; 16];
1484 header[4..8].copy_from_slice(b"ftyp");
1485 header[8..12].copy_from_slice(b"heic");
1486 assert_eq!(detect_from_magic(&header), Some(FileType::Heif));
1487 }
1488
1489 #[test]
1490 fn test_detect_avif() {
1491 let mut header = [0u8; 16];
1492 header[4..8].copy_from_slice(b"ftyp");
1493 header[8..12].copy_from_slice(b"avif");
1494 assert_eq!(detect_from_magic(&header), Some(FileType::Avif));
1495 }
1496
1497 #[test]
1498 fn test_detect_cr3() {
1499 let mut header = [0u8; 16];
1500 header[4..8].copy_from_slice(b"ftyp");
1501 header[8..12].copy_from_slice(b"crx ");
1502 assert_eq!(detect_from_magic(&header), Some(FileType::Cr3));
1503 }
1504
1505 #[test]
1506 fn test_detect_flac() {
1507 assert_eq!(detect_from_magic(b"fLaC\x00\x00"), Some(FileType::Flac));
1508 }
1509
1510 #[test]
1511 fn test_detect_ogg() {
1512 assert_eq!(detect_from_magic(b"OggS\x00\x02"), Some(FileType::Ogg));
1513 }
1514
1515 #[test]
1516 fn test_detect_mp3_id3() {
1517 assert_eq!(detect_from_magic(b"ID3\x04\x00"), Some(FileType::Mp3));
1518 }
1519
1520 #[test]
1521 fn test_detect_rar() {
1522 assert_eq!(detect_from_magic(b"Rar!\x1A\x07\x01\x00"), Some(FileType::Rar));
1523 }
1524
1525 #[test]
1526 fn test_detect_7z() {
1527 assert_eq!(detect_from_magic(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]), Some(FileType::SevenZ));
1528 }
1529
1530 #[test]
1531 fn test_detect_gzip() {
1532 assert_eq!(detect_from_magic(&[0x1F, 0x8B, 0x08, 0x00]), Some(FileType::Gzip));
1533 }
1534
1535 #[test]
1536 fn test_detect_raf() {
1537 assert_eq!(detect_from_magic(b"FUJIFILMCCD-RAW 0201"), Some(FileType::Raf));
1538 }
1539
1540 #[test]
1541 fn test_detect_psd() {
1542 assert_eq!(detect_from_magic(b"8BPS\x00\x01"), Some(FileType::Psd));
1543 }
1544
1545 #[test]
1546 fn test_detect_from_extension() {
1547 assert_eq!(detect_from_extension("jpg"), Some(FileType::Jpeg));
1548 assert_eq!(detect_from_extension(".JPEG"), Some(FileType::Jpeg));
1549 assert_eq!(detect_from_extension("cr2"), Some(FileType::Cr2));
1550 assert_eq!(detect_from_extension("cr3"), Some(FileType::Cr3));
1551 assert_eq!(detect_from_extension("nef"), Some(FileType::Nef));
1552 assert_eq!(detect_from_extension("arw"), Some(FileType::Arw));
1553 assert_eq!(detect_from_extension("dng"), Some(FileType::Dng));
1554 assert_eq!(detect_from_extension("raf"), Some(FileType::Raf));
1555 assert_eq!(detect_from_extension("mp4"), Some(FileType::Mp4));
1556 assert_eq!(detect_from_extension("mov"), Some(FileType::QuickTime));
1557 assert_eq!(detect_from_extension("flac"), Some(FileType::Flac));
1558 assert_eq!(detect_from_extension("docx"), Some(FileType::Docx));
1559 assert_eq!(detect_from_extension("xlsx"), Some(FileType::Xlsx));
1560 assert_eq!(detect_from_extension("3fr"), Some(FileType::ThreeFR));
1561 assert_eq!(detect_from_extension("xyz"), None);
1562 }
1563
1564 #[test]
1565 fn test_all_types_have_extensions() {
1566 for &ft in FileType::all() {
1567 assert!(!ft.extensions().is_empty(), "{:?} has no extensions", ft);
1568 }
1569 }
1570
1571 #[test]
1572 fn test_all_types_have_mime() {
1573 for &ft in FileType::all() {
1574 assert!(!ft.mime_type().is_empty(), "{:?} has no MIME type", ft);
1575 }
1576 }
1577
1578 #[test]
1579 fn test_total_format_count() {
1580 assert!(FileType::all().len() >= 100, "Expected 100+ formats, got {}", FileType::all().len());
1581 }
1582}