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::Fpf => "FPF",
333 FileType::Ods => "ODS",
334 FileType::Odt => "ODT",
335 FileType::Odp => "ODP",
336 FileType::Odg => "ODG",
337 FileType::Odf => "ODF",
338 FileType::Odb => "ODB",
339 FileType::Odi => "ODI",
340 FileType::Odc => "ODC",
341 FileType::Eip => "EIP",
342 }
343 }
344
345 pub fn mime_type(self) -> &'static str {
347 match self {
348 FileType::Jpeg => "image/jpeg",
349 FileType::Tiff | FileType::Btf => "image/tiff",
350 FileType::Png => "image/png",
351 FileType::Gif => "image/gif",
352 FileType::Bmp => "image/bmp",
353 FileType::WebP => "image/webp",
354 FileType::Heif => "image/heif",
355 FileType::Avif => "image/avif",
356 FileType::Psd => "image/vnd.adobe.photoshop",
357 FileType::Jp2 => "image/jp2",
358 FileType::J2c => "image/x-j2c",
359 FileType::Jxl => "image/jxl",
360 FileType::Jxr => "image/jxr",
361 FileType::Flif => "image/flif",
362 FileType::Bpg => "image/bpg",
363 FileType::Exr => "image/x-exr",
364 FileType::Ico => "image/x-icon",
365 FileType::Jps => "image/x-jps",
366 FileType::DjVu => "image/vnd.djvu",
367 FileType::Xcf => "image/x-xcf",
368 FileType::Pcx => "image/x-pcx",
369 FileType::Pict => "image/x-pict",
370 FileType::Psp => "image/x-psp",
371 FileType::Hdr => "image/vnd.radiance",
372 FileType::Rwz => "image/x-rawzor",
373 FileType::Mng => "video/x-mng",
374 FileType::PhotoCd => "image/x-photo-cd",
375 FileType::Cr2 => "image/x-canon-cr2",
377 FileType::Cr3 | FileType::Crm => "image/x-canon-cr3",
378 FileType::Crw => "image/x-canon-crw",
379 FileType::Nef => "image/x-nikon-nef",
380 FileType::Arw | FileType::Arq => "image/x-sony-arw",
381 FileType::Sr2 => "image/x-sony-sr2",
382 FileType::Srf => "image/x-sony-srf",
383 FileType::Orf => "image/x-olympus-orf",
384 FileType::Rw2 => "image/x-panasonic-rw2",
385 FileType::Dng | FileType::Gpr => "image/x-adobe-dng",
386 FileType::Raf => "image/x-fuji-raf",
387 FileType::Pef => "image/x-pentax-pef",
388 FileType::Dcr => "image/x-kodak-dcr",
389 FileType::Mrw => "image/x-minolta-mrw",
390 FileType::Erf => "image/x-epson-erf",
391 FileType::Fff | FileType::ThreeFR => "image/x-hasselblad-fff",
392 FileType::Iiq => "image/x-phaseone-iiq",
393 FileType::Rwl => "image/x-leica-rwl",
394 FileType::Mef => "image/x-mamiya-mef",
395 FileType::Srw => "image/x-samsung-srw",
396 FileType::X3f => "image/x-sigma-x3f",
397 FileType::Mp4 | FileType::F4v => "video/mp4",
399 FileType::QuickTime | FileType::Mqv => "video/quicktime",
400 FileType::Avi => "video/x-msvideo",
401 FileType::Mkv => "video/x-matroska",
402 FileType::WebM => "video/webm",
403 FileType::Wmv => "video/x-ms-wmv",
404 FileType::Asf => "video/x-ms-asf",
405 FileType::Flv => "video/x-flv",
406 FileType::Mxf => "application/mxf",
407 FileType::Czi => "image/czi",
408 FileType::M2ts => "video/mp2t",
409 FileType::Mpeg => "video/mpeg",
410 FileType::ThreeGP => "video/3gpp",
411 FileType::RealMedia => "application/vnd.rn-realmedia",
412 FileType::R3d => "video/x-red-r3d",
413 FileType::Dvb => "video/dvb",
414 FileType::Lrv => "video/mp4",
415 FileType::Wtv => "video/x-ms-wtv",
416 FileType::DvrMs => "video/x-ms-dvr",
417 FileType::Mp3 => "audio/mpeg",
419 FileType::Flac => "audio/flac",
420 FileType::Ogg | FileType::Opus => "audio/ogg",
421 FileType::Wav => "audio/wav",
422 FileType::Aiff => "audio/x-aiff",
423 FileType::Aac => "audio/aac",
424 FileType::Mpc => "audio/x-musepack",
425 FileType::Ape => "audio/x-ape",
426 FileType::WavPack => "audio/x-wavpack",
427 FileType::Ofr => "audio/x-ofr",
428 FileType::Dsf => "audio/dsf",
429 FileType::Audible => "audio/x-pn-audibleaudio",
430 FileType::RealAudio => "audio/x-pn-realaudio",
431 FileType::Wma => "audio/x-ms-wma",
432 FileType::M4a => "audio/mp4",
433 FileType::Dss => "audio/x-dss",
434 FileType::Pdf => "application/pdf",
436 FileType::PostScript => "application/postscript",
437 FileType::Doc => "application/msword",
438 FileType::Docx => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
439 FileType::Xls => "application/vnd.ms-excel",
440 FileType::Xlsx => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
441 FileType::Ppt => "application/vnd.ms-powerpoint",
442 FileType::Pptx => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
443 FileType::Numbers => "application/x-iwork-numbers-sffnumbers",
444 FileType::Pages => "application/x-iwork-pages-sffpages",
445 FileType::Key => "application/x-iwork-keynote-sffkey",
446 FileType::InDesign => "application/x-indesign",
447 FileType::Rtf => "application/rtf",
448 FileType::Zip => "application/zip",
450 FileType::Rar => "application/x-rar-compressed",
451 FileType::SevenZ => "application/x-7z-compressed",
452 FileType::Gzip => "application/x-gzip",
453 FileType::Xmp => "application/rdf+xml",
455 FileType::Mie => "application/x-mie",
456 FileType::Exv => "application/x-exv",
457 FileType::Vrd => "application/octet-stream",
458 FileType::Dr4 => "application/octet-stream",
459 FileType::Icc => "application/vnd.icc.profile",
460 FileType::Html => "text/html",
461 FileType::Exe => "application/x-dosexec",
462 FileType::Font => "font/sfnt",
463 FileType::Swf => "application/x-shockwave-flash",
464 FileType::Dicom => "application/dicom",
465 FileType::Fits => "application/fits",
466 FileType::Mrc => "image/x-mrc",
467 FileType::Moi => "application/octet-stream",
468 FileType::MacOs => "application/unknown",
469 FileType::Json => "application/json",
470 FileType::Pcap => "application/vnd.tcpdump.pcap",
471 FileType::Pcapng => "application/vnd.tcpdump.pcap",
472 FileType::Svg => "image/svg+xml",
473 FileType::Pgf => "image/pgf",
474 FileType::Xisf => "application/xisf",
475 FileType::Torrent => "application/x-bittorrent",
476 FileType::Mobi => "application/x-mobipocket-ebook",
477 FileType::SonyPmp => "image/x-sony-pmp",
478 FileType::Plist => "application/x-plist",
479 FileType::Aae => "application/vnd.apple.photos",
480 FileType::KyoceraRaw => "image/x-raw",
481 FileType::Lfp => "image/x-lytro-lfp",
482 FileType::PortableFloatMap => "image/x-pfm",
483 FileType::Fpf => "image/x-flir-fpf",
484 FileType::Ods => "application/vnd.oasis.opendocument.spreadsheet",
485 FileType::Odt => "application/vnd.oasis.opendocument.text",
486 FileType::Odp => "application/vnd.oasis.opendocument.presentation",
487 FileType::Odg => "application/vnd.oasis.opendocument.graphics",
488 FileType::Odf => "application/vnd.oasis.opendocument.formula",
489 FileType::Odb => "application/vnd.oasis.opendocument.database",
490 FileType::Odi => "application/vnd.oasis.opendocument.image",
491 FileType::Odc => "application/vnd.oasis.opendocument.chart",
492 FileType::Eip => "application/x-captureone",
493 }
494 }
495
496 pub fn extensions(self) -> &'static [&'static str] {
498 match self {
499 FileType::Jpeg => &["jpg", "jpeg", "jpe", "jif", "jfif"],
500 FileType::Tiff => &["tif", "tiff"],
501 FileType::Png => &["png"],
502 FileType::Gif => &["gif"],
503 FileType::Bmp => &["bmp", "dib"],
504 FileType::WebP => &["webp"],
505 FileType::Heif => &["heif", "heic", "hif"],
506 FileType::Avif => &["avif"],
507 FileType::Psd => &["psd", "psb", "psdt"],
508 FileType::Jp2 => &["jp2", "jpf", "jpm", "jpx", "jph"],
509 FileType::J2c => &["j2c", "j2k", "jpc"],
510 FileType::Jxl => &["jxl"],
511 FileType::Jxr => &["jxr", "hdp", "wdp"],
512 FileType::Flif => &["flif"],
513 FileType::Bpg => &["bpg"],
514 FileType::Exr => &["exr"],
515 FileType::Ico => &["ico", "cur"],
516 FileType::Jps => &["jps"],
517 FileType::DjVu => &["djvu", "djv"],
518 FileType::Xcf => &["xcf"],
519 FileType::Pcx => &["pcx"],
520 FileType::Pict => &["pict", "pct"],
521 FileType::Psp => &["psp", "pspimage"],
522 FileType::Hdr => &["hdr"],
523 FileType::Rwz => &["rwz"],
524 FileType::Btf => &["btf"],
525 FileType::Mng => &["mng", "jng"],
526 FileType::PhotoCd => &["pcd"],
527 FileType::Cr2 => &["cr2"],
529 FileType::Cr3 => &["cr3"],
530 FileType::Crw => &["crw", "ciff"],
531 FileType::Nef => &["nef", "nrw"],
532 FileType::Arw => &["arw"],
533 FileType::Sr2 => &["sr2"],
534 FileType::Srf => &["srf"],
535 FileType::Orf => &["orf", "ori"],
536 FileType::Rw2 => &["rw2"],
537 FileType::Dng => &["dng"],
538 FileType::Raf => &["raf"],
539 FileType::Pef => &["pef"],
540 FileType::Dcr => &["dcr"],
541 FileType::Mrw => &["mrw"],
542 FileType::Erf => &["erf"],
543 FileType::Fff => &["fff"],
544 FileType::Iiq => &["iiq"],
545 FileType::Rwl => &["rwl"],
546 FileType::Mef => &["mef"],
547 FileType::Srw => &["srw"],
548 FileType::X3f => &["x3f"],
549 FileType::Gpr => &["gpr"],
550 FileType::Arq => &["arq"],
551 FileType::ThreeFR => &["3fr"],
552 FileType::Crm => &["crm"],
553 FileType::Mp4 => &["mp4", "m4v"],
555 FileType::QuickTime => &["mov", "qt"],
556 FileType::Avi => &["avi"],
557 FileType::Mkv => &["mkv", "mks"],
558 FileType::WebM => &["webm"],
559 FileType::Wmv => &["wmv"],
560 FileType::Asf => &["asf"],
561 FileType::Flv => &["flv"],
562 FileType::Mxf => &["mxf"],
563 FileType::Czi => &["czi"],
564 FileType::M2ts => &["m2ts", "mts", "m2t", "ts"],
565 FileType::Mpeg => &["mpg", "mpeg", "m2v", "mpv"],
566 FileType::ThreeGP => &["3gp", "3gpp", "3g2", "3gp2"],
567 FileType::RealMedia => &["rm", "rv", "rmvb"],
568 FileType::R3d => &["r3d"],
569 FileType::Dvb => &["dvb"],
570 FileType::Lrv => &["lrv", "lrf"],
571 FileType::Mqv => &["mqv"],
572 FileType::F4v => &["f4v", "f4a", "f4b", "f4p"],
573 FileType::Wtv => &["wtv"],
574 FileType::DvrMs => &["dvr-ms"],
575 FileType::Mp3 => &["mp3"],
577 FileType::Flac => &["flac"],
578 FileType::Ogg => &["ogg", "oga", "ogv"],
579 FileType::Wav => &["wav"],
580 FileType::Aiff => &["aiff", "aif", "aifc"],
581 FileType::Aac => &["aac"],
582 FileType::Opus => &["opus"],
583 FileType::Mpc => &["mpc"],
584 FileType::Ape => &["ape"],
585 FileType::WavPack => &["wv", "wvp"],
586 FileType::Ofr => &["ofr"],
587 FileType::Dsf => &["dsf"],
588 FileType::Audible => &["aa", "aax"],
589 FileType::RealAudio => &["ra"],
590 FileType::Wma => &["wma"],
591 FileType::M4a => &["m4a", "m4b", "m4p"],
592 FileType::Dss => &["dss"],
593 FileType::Pdf => &["pdf"],
595 FileType::PostScript => &["ps", "eps", "epsf"],
596 FileType::Doc => &["doc", "dot"],
597 FileType::Docx => &["docx", "docm"],
598 FileType::Xls => &["xls", "xlt"],
599 FileType::Xlsx => &["xlsx", "xlsm", "xlsb"],
600 FileType::Ppt => &["ppt", "pps", "pot"],
601 FileType::Pptx => &["pptx", "pptm"],
602 FileType::Numbers => &["numbers", "nmbtemplate"],
603 FileType::Pages => &["pages"],
604 FileType::Key => &["key", "kth"],
605 FileType::InDesign => &["ind", "indd", "indt"],
606 FileType::Rtf => &["rtf"],
607 FileType::Zip => &["zip"],
609 FileType::Rar => &["rar"],
610 FileType::SevenZ => &["7z"],
611 FileType::Gzip => &["gz", "gzip"],
612 FileType::Xmp => &["xmp", "inx", "xml"],
614 FileType::Mie => &["mie"],
615 FileType::Exv => &["exv"],
616 FileType::Vrd => &["vrd"],
617 FileType::Dr4 => &["dr4"],
618 FileType::Icc => &["icc", "icm"],
619 FileType::Html => &["html", "htm", "xhtml", "svg"],
620 FileType::Exe => &["exe", "dll", "elf", "so", "dylib", "a", "macho", "o"],
621 FileType::Font => &["ttf", "otf", "woff", "woff2", "ttc", "dfont", "afm", "pfa", "pfb"],
622 FileType::Swf => &["swf"],
623 FileType::Dicom => &["dcm"],
624 FileType::Fits => &["fits", "fit", "fts"],
625 FileType::Mrc => &["mrc"],
626 FileType::Moi => &["moi"],
627 FileType::MacOs => &["macos"],
628 FileType::Json => &["json"],
629 FileType::Pcap => &["pcap", "cap"],
630 FileType::Pcapng => &["pcapng", "ntar"],
631 FileType::Svg => &["svg"],
632 FileType::Pgf => &["pgf"],
633 FileType::Xisf => &["xisf"],
634 FileType::Torrent => &["torrent"],
635 FileType::Mobi => &["mobi", "azw", "azw3"],
636 FileType::SonyPmp => &["pmp"],
637 FileType::Plist => &["plist"],
638 FileType::Aae => &["aae"],
639 FileType::KyoceraRaw => &["raw"],
640 FileType::PortableFloatMap => &["pfm"],
641 FileType::Fpf => &["fpf"],
642 FileType::Ods => &["ods"],
643 FileType::Odt => &["odt"],
644 FileType::Odp => &["odp"],
645 FileType::Odg => &["odg"],
646 FileType::Odf => &["odf"],
647 FileType::Odb => &["odb"],
648 FileType::Odi => &["odi"],
649 FileType::Odc => &["odc"],
650 FileType::Lfp => &["lfp", "lfr"],
651 FileType::Eip => &["eip"],
652 }
653 }
654
655 pub fn support(self) -> Support {
657 match self {
658 FileType::Xmp | FileType::Mie | FileType::Exv => Support::ReadWriteCreate,
660 FileType::Jpeg
662 | FileType::Tiff
663 | FileType::Png
664 | FileType::Gif
665 | FileType::WebP
666 | FileType::Heif
667 | FileType::Avif
668 | FileType::Psd
669 | FileType::Jp2
670 | FileType::Jxl
671 | FileType::Jxr
672 | FileType::Flif
673 | FileType::Cr2
674 | FileType::Cr3
675 | FileType::Crw
676 | FileType::Nef
677 | FileType::Arw
678 | FileType::Arq
679 | FileType::Sr2
680 | FileType::Orf
681 | FileType::Rw2
682 | FileType::Dng
683 | FileType::Raf
684 | FileType::Pef
685 | FileType::Erf
686 | FileType::Fff
687 | FileType::Iiq
688 | FileType::Rwl
689 | FileType::Mef
690 | FileType::Srw
691 | FileType::X3f
692 | FileType::Gpr
693 | FileType::Crm
694 | FileType::Mp4
695 | FileType::QuickTime
696 | FileType::ThreeGP
697 | FileType::Dvb
698 | FileType::Lrv
699 | FileType::Mqv
700 | FileType::F4v
701 | FileType::Pdf
702 | FileType::PostScript
703 | FileType::InDesign
704 | FileType::Vrd
705 | FileType::Dr4
706 | FileType::Audible => Support::ReadWrite,
707 _ => Support::Read,
709 }
710 }
711
712 pub fn all() -> &'static [FileType] {
714 ALL_FILE_TYPES
715 }
716}
717
718static ALL_FILE_TYPES: &[FileType] = &[
719 FileType::Jpeg, FileType::Tiff, FileType::Png, FileType::Gif, FileType::Bmp,
721 FileType::WebP, FileType::Heif, FileType::Avif, FileType::Psd, FileType::Jp2,
722 FileType::J2c, FileType::Jxl, FileType::Jxr, FileType::Flif, FileType::Bpg,
723 FileType::Exr, FileType::Ico, FileType::Jps,
724 FileType::DjVu, FileType::Xcf, FileType::Pcx, FileType::Pict, FileType::Psp,
726 FileType::Hdr, FileType::Rwz, FileType::Btf, FileType::Mng, FileType::PhotoCd,
727 FileType::Cr2, FileType::Cr3, FileType::Crw, FileType::Nef, FileType::Arw,
729 FileType::Sr2, FileType::Srf, FileType::Orf, FileType::Rw2, FileType::Dng,
730 FileType::Raf, FileType::Pef, FileType::Dcr, FileType::Mrw, FileType::Erf,
731 FileType::Fff, FileType::Iiq, FileType::Rwl, FileType::Mef, FileType::Srw,
732 FileType::X3f, FileType::Gpr, FileType::Arq, FileType::ThreeFR, FileType::Crm,
733 FileType::Mp4, FileType::QuickTime, FileType::Avi, FileType::Mkv, FileType::WebM,
735 FileType::Wmv, FileType::Asf, FileType::Flv, FileType::Mxf, FileType::M2ts,
736 FileType::Mpeg, FileType::ThreeGP, FileType::RealMedia, FileType::R3d,
737 FileType::Dvb, FileType::Lrv, FileType::Mqv, FileType::F4v, FileType::Wtv,
738 FileType::DvrMs,
739 FileType::Mp3, FileType::Flac, FileType::Ogg, FileType::Wav, FileType::Aiff,
741 FileType::Aac, FileType::Opus, FileType::Mpc, FileType::Ape, FileType::WavPack,
742 FileType::Ofr, FileType::Dsf, FileType::Audible, FileType::RealAudio,
743 FileType::Wma, FileType::M4a, FileType::Dss,
744 FileType::Pdf, FileType::PostScript, FileType::Doc, FileType::Docx,
746 FileType::Xls, FileType::Xlsx, FileType::Ppt, FileType::Pptx,
747 FileType::Numbers, FileType::Pages, FileType::Key,
748 FileType::InDesign, FileType::Rtf,
749 FileType::Zip, FileType::Rar, FileType::SevenZ, FileType::Gzip,
751 FileType::Xmp, FileType::Mie, FileType::Exv, FileType::Vrd, FileType::Dr4, FileType::Icc,
753 FileType::Html, FileType::Exe, FileType::Font, FileType::Swf,
754 FileType::Dicom, FileType::Fits,
755 FileType::Mrc, FileType::Moi, FileType::MacOs, FileType::Json,
756 FileType::Pcap, FileType::Pcapng,
757 FileType::Svg,
758 FileType::Pgf, FileType::Xisf, FileType::Torrent, FileType::Mobi, FileType::SonyPmp,
759 FileType::Plist, FileType::Aae, FileType::KyoceraRaw,
760 FileType::PortableFloatMap,
761 FileType::Fpf,
762 FileType::Lfp,
763 FileType::Ods, FileType::Odt, FileType::Odp, FileType::Odg,
765 FileType::Odf, FileType::Odb, FileType::Odi, FileType::Odc,
766 FileType::Eip,
768];
769
770pub fn detect_from_magic(header: &[u8]) -> Option<FileType> {
772 if header.len() < 4 {
773 return None;
774 }
775
776 if header.starts_with(&[0xFF, 0xD8, 0xFF]) {
780 return Some(FileType::Jpeg);
781 }
782
783 if header.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
785 return Some(FileType::Png);
786 }
787
788 if header.starts_with(&[0x89, 0x4C, 0x46, 0x50, 0x0D, 0x0A, 0x1A, 0x0A]) {
790 return Some(FileType::Lfp);
791 }
792
793 if header.starts_with(b"GIF8") && header.len() >= 6 && (header[4] == b'7' || header[4] == b'9') {
795 return Some(FileType::Gif);
796 }
797
798 if header.len() >= 8 && header.starts_with(b"IIII")
800 && (header[4] == 0x04 || header[4] == 0x05) && header[5] == 0x00
801 && header[6] == 0x04 && header[7] == 0x00
802 {
803 return Some(FileType::Dr4);
804 }
805
806 if header.starts_with(b"CANON OPTIONAL DATA\0") {
808 return Some(FileType::Vrd);
809 }
810
811 if header.len() >= 4 {
813 let is_le = header[0] == b'I' && header[1] == b'I' && header[2] == 0x2A && header[3] == 0x00;
814 let is_be = header[0] == b'M' && header[1] == b'M' && header[2] == 0x00 && header[3] == 0x2A;
815 if is_le || is_be {
816 if header.len() >= 10 && is_le && header[8] == b'C' && header[9] == b'R' {
818 return Some(FileType::Cr2);
819 }
820 if header.len() >= 12 && is_le && &header[8..12] == b"IIII" {
822 return Some(FileType::Iiq);
823 }
824 if header.len() >= 12 && is_be && &header[8..12] == b"MMMM" {
825 return Some(FileType::Iiq);
826 }
827 if header.len() >= 4 && is_le && header[0] == b'I' && header[1] == b'I' {
829 if header.len() >= 8 {
830 }
833 }
834 return Some(FileType::Tiff);
837 }
838 let is_btf_le = header[0] == b'I' && header[1] == b'I' && header[2] == 0x2B && header[3] == 0x00;
840 let is_btf_be = header[0] == b'M' && header[1] == b'M' && header[2] == 0x00 && header[3] == 0x2B;
841 if is_btf_le || is_btf_be {
842 return Some(FileType::Btf);
843 }
844 }
845
846 if header.starts_with(b"BM") && header.len() >= 6 {
848 return Some(FileType::Bmp);
849 }
850
851 if header.len() >= 12 && header.starts_with(b"RIFF") {
853 match &header[8..12] {
854 b"WEBP" => return Some(FileType::WebP),
855 b"AVI " => return Some(FileType::Avi),
856 b"WAVE" => return Some(FileType::Wav),
857 _ => {}
858 }
859 }
860
861 if header.starts_with(b"8BPS") {
863 return Some(FileType::Psd);
864 }
865
866 if header.len() >= 12 && header.starts_with(&[0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20]) {
868 return Some(FileType::Jp2);
869 }
870
871 if header.starts_with(&[0xFF, 0x4F, 0xFF, 0x51]) {
873 return Some(FileType::J2c);
874 }
875
876 if header.len() >= 2 && header[0] == 0xFF && header[1] == 0x0A {
878 return Some(FileType::Jxl);
879 }
880 if header.len() >= 12 && header.starts_with(&[0x00, 0x00, 0x00, 0x0C, 0x4A, 0x58, 0x4C, 0x20]) {
881 return Some(FileType::Jxl);
882 }
883
884 if header.starts_with(b"FLIF") {
886 return Some(FileType::Flif);
887 }
888
889 if header.starts_with(&[0x42, 0x50, 0x47, 0xFB]) {
891 return Some(FileType::Bpg);
892 }
893
894 if header.starts_with(&[0x76, 0x2F, 0x31, 0x01]) {
896 return Some(FileType::Exr);
897 }
898
899 if header.len() >= 4 && header[0] == 0 && header[1] == 0 && (header[2] == 1 || header[2] == 2) && header[3] == 0 {
901 return Some(FileType::Ico);
902 }
903
904 if header.len() >= 8 && header.starts_with(b"AT&TFORM") {
906 return Some(FileType::DjVu);
907 }
908
909 if header.starts_with(b"gimp xcf") {
911 return Some(FileType::Xcf);
912 }
913
914 if header.starts_with(&[0x8A, 0x4D, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
916 return Some(FileType::Mng);
917 }
918
919 if header.starts_with(&[0x8B, 0x4A, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
921 return Some(FileType::Mng);
922 }
923
924 if header.len() >= 10 && header.starts_with(b"#?RADIANCE") {
926 return Some(FileType::Hdr);
927 }
928
929 if header.len() >= 3 && header[0] == b'P' && (header[1] == b'F' || header[1] == b'f') && header[2] == b'\n' {
931 return Some(FileType::PortableFloatMap);
932 }
933
934 if header.starts_with(b"FPF Public Image Format\0") {
936 return Some(FileType::Fpf);
937 }
938
939 if header.len() >= 15 && header.starts_with(b"FUJIFILMCCD-RAW") {
943 return Some(FileType::Raf);
944 }
945
946 if header.len() >= 14 && header[0] == b'I' && header[1] == b'I'
948 && header[2] == 0x1A && header[3] == 0x00
949 && &header[6..14] == b"HEAPCCDR"
950 {
951 return Some(FileType::Crw);
952 }
953
954 if header.starts_with(&[0x00, 0x4D, 0x52, 0x4D]) {
956 return Some(FileType::Mrw);
957 }
958
959 if header.starts_with(b"FOVb") {
961 return Some(FileType::X3f);
962 }
963
964 if header.len() >= 4 && header[0] == b'I' && header[1] == b'I' && header[2] == 0x55 && header[3] == 0x00 {
966 return Some(FileType::Rw2);
967 }
968
969 if header.len() >= 12 && &header[4..8] == b"ftyp" {
973 let brand = &header[8..12];
974 if brand == b"heic" || brand == b"mif1" || brand == b"heim" || brand == b"heis"
976 || brand == b"msf1"
977 {
978 return Some(FileType::Heif);
979 }
980 if brand == b"avif" || brand == b"avis" {
982 return Some(FileType::Avif);
983 }
984 if brand == b"crx " {
986 return Some(FileType::Cr3);
987 }
988 if brand == b"qt " {
990 return Some(FileType::QuickTime);
991 }
992 if brand == b"3gp4" || brand == b"3gp5" || brand == b"3gp6" || brand == b"3g2a" {
994 return Some(FileType::ThreeGP);
995 }
996 if brand == b"M4A " || brand == b"M4B " || brand == b"M4P " {
998 return Some(FileType::M4a);
999 }
1000 if brand == b"M4V " || brand == b"M4VH" || brand == b"M4VP" {
1001 return Some(FileType::Mp4);
1002 }
1003 if brand == b"F4V " || brand == b"F4P " {
1005 return Some(FileType::F4v);
1006 }
1007 return Some(FileType::Mp4);
1009 }
1010
1011 if header.len() >= 8 {
1013 let atom_type = &header[4..8];
1014 if atom_type == b"moov" || atom_type == b"mdat" || atom_type == b"wide"
1015 || atom_type == b"free" || atom_type == b"pnot" || atom_type == b"skip"
1016 {
1017 return Some(FileType::QuickTime);
1018 }
1019 }
1020
1021 if header.starts_with(&[0x1A, 0x45, 0xDF, 0xA3]) {
1023 return Some(FileType::Mkv);
1024 }
1026
1027 if header.starts_with(b"FLV\x01") {
1029 return Some(FileType::Flv);
1030 }
1031
1032 if header.len() >= 16 && header.starts_with(&[0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11]) {
1034 return Some(FileType::Asf);
1035 }
1037
1038 if header.len() >= 8 && header.starts_with(&[0x06, 0x0E, 0x2B, 0x34]) {
1040 return Some(FileType::Mxf);
1041 }
1042
1043 if header.len() >= 10 && header.starts_with(b"ZISRAWFILE") {
1045 return Some(FileType::Czi);
1046 }
1047
1048 if header.len() >= 40 && &header[36..40] == b"acsp" {
1050 return Some(FileType::Icc);
1051 }
1052
1053 if header.len() >= 4 && header[0] == 0 && header[1] == 0 && header[2] == 1
1055 && (header[3] == 0xBA || header[3] == 0xBB || (header[3] & 0xF0) == 0xE0)
1056 {
1057 return Some(FileType::Mpeg);
1058 }
1059
1060 if header.len() >= 1 && header[0] == 0x47 {
1062 if header.len() >= 376 && header[188] == 0x47 {
1063 return Some(FileType::M2ts);
1064 }
1065 if header.len() >= 384 && header[192] == 0x47 {
1066 return Some(FileType::M2ts);
1067 }
1068 }
1069
1070 if header.starts_with(b".RMF") {
1072 return Some(FileType::RealMedia);
1073 }
1074
1075 if header.starts_with(b"RED1") || header.starts_with(b"RED2") {
1077 return Some(FileType::R3d);
1078 }
1079
1080 if header.starts_with(b"ID3") {
1084 return Some(FileType::Mp3);
1085 }
1086 if header.len() >= 2 && header[0] == 0xFF
1090 && (header[1] == 0xF0 || header[1] == 0xF1
1091 || header[1] == 0xF8 || header[1] == 0xF9)
1092 {
1093 return Some(FileType::Aac);
1094 }
1095 if header.len() >= 2 && header[0] == 0xFF && (header[1] & 0xE0) == 0xE0 {
1097 return Some(FileType::Mp3);
1098 }
1099
1100 if header.starts_with(b"fLaC") {
1102 return Some(FileType::Flac);
1103 }
1104
1105 if header.starts_with(b"OggS") {
1107 return Some(FileType::Ogg);
1108 }
1109
1110 if header.len() >= 12 && header.starts_with(b"FORM") {
1112 if &header[8..12] == b"AIFF" || &header[8..12] == b"AIFC" {
1113 return Some(FileType::Aiff);
1114 }
1115 }
1116
1117 if header.starts_with(b"MAC ") {
1119 return Some(FileType::Ape);
1120 }
1121
1122 if header.len() >= 0x20 && &header[0x19..0x20] == b"ARECOYK" {
1124 return Some(FileType::KyoceraRaw);
1125 }
1126
1127 if header.starts_with(b"MP+") || header.starts_with(b"MPCK") {
1129 return Some(FileType::Mpc);
1130 }
1131
1132 if header.starts_with(b"wvpk") {
1134 return Some(FileType::WavPack);
1135 }
1136
1137 if header.starts_with(b"DSD ") {
1139 return Some(FileType::Dsf);
1140 }
1141
1142 if header.starts_with(b"OFR ") {
1144 return Some(FileType::Ofr);
1145 }
1146
1147 if header.len() >= 4 && header[0] == b'.' && header[1] == b'r' && header[2] == b'a' && header[3] == 0xFD {
1149 return Some(FileType::RealAudio);
1150 }
1151
1152 if header.len() >= 4
1154 && (header[0] == 0x02 || header[0] == 0x03)
1155 && (header[1] == b'd')
1156 && (header[2] == b's')
1157 && (header[3] == b's' || header[3] == b'2')
1158 {
1159 return Some(FileType::Dss);
1160 }
1161
1162 if header.starts_with(b"%PDF-") {
1166 return Some(FileType::Pdf);
1167 }
1168
1169 if header.starts_with(b"%!PS") || header.starts_with(b"%!Adobe") {
1171 return Some(FileType::PostScript);
1172 }
1173
1174 if header.starts_with(&[0xD0, 0xCF, 0x11, 0xE0]) {
1176 return Some(FileType::Doc); }
1178
1179 if header.starts_with(b"{\\rtf") {
1181 return Some(FileType::Rtf);
1182 }
1183
1184 if header.len() >= 8 && header.starts_with(&[0x06, 0x06, 0xED, 0xF5, 0xD8, 0x1D, 0x46, 0xE5]) {
1186 return Some(FileType::InDesign);
1187 }
1188
1189 if header.starts_with(&[0x50, 0x4B, 0x03, 0x04]) {
1193 return Some(FileType::Zip);
1194 }
1196
1197 if header.len() >= 6 && header.starts_with(b"Rar!\x1A\x07") {
1199 return Some(FileType::Rar);
1200 }
1201
1202 if header.len() >= 6 && header.starts_with(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]) {
1204 return Some(FileType::SevenZ);
1205 }
1206
1207 if header.len() >= 2 && header[0] == 0x1F && header[1] == 0x8B {
1209 return Some(FileType::Gzip);
1210 }
1211
1212 if header.len() >= 4 && header[0] == 0x0A && header[1] == 0x0D && header[2] == 0x0D && header[3] == 0x0A {
1214 return Some(FileType::Pcapng);
1215 }
1216
1217 if header.len() >= 4 && (
1219 (header[0] == 0xD4 && header[1] == 0xC3 && header[2] == 0xB2 && header[3] == 0xA1) ||
1220 (header[0] == 0xA1 && header[1] == 0xB2 && header[2] == 0xC3 && header[3] == 0xD4)
1221 ) {
1222 return Some(FileType::Pcap);
1223 }
1224
1225 if header.len() >= 3
1229 && ((header[0] == b'F' || header[0] == b'C' || header[0] == b'Z')
1230 && header[1] == b'W'
1231 && header[2] == b'S')
1232 {
1233 return Some(FileType::Swf);
1234 }
1235
1236 if header.len() >= 132 && &header[128..132] == b"DICM" {
1238 return Some(FileType::Dicom);
1239 }
1240
1241
1242 if header.len() >= 214 {
1244 let ax1 = u32::from_le_bytes([header[64], header[65], header[66], header[67]]);
1245 let ax2 = u32::from_le_bytes([header[68], header[69], header[70], header[71]]);
1246 let ax3 = u32::from_le_bytes([header[72], header[73], header[74], header[75]]);
1247 if (1..=3).contains(&ax1) && (1..=3).contains(&ax2) && (1..=3).contains(&ax3)
1248 && &header[208..211] == b"MAP"
1249 {
1250 let ms0 = header[212];
1251 let ms1 = header[213];
1252 if (ms0 == 0x44 && ms1 == 0x44)
1253 || (ms0 == 0x44 && ms1 == 0x41)
1254 || (ms0 == 0x11 && ms1 == 0x11)
1255 {
1256 return Some(FileType::Mrc);
1257 }
1258 }
1259 }
1260
1261 if header.len() >= 9 && header.starts_with(b"SIMPLE =") {
1263 return Some(FileType::Fits);
1264 }
1265
1266 if header.len() >= 4 && header[0] == 0x7E && header[1] == 0x10 && header[2] == 0x04 {
1268 return Some(FileType::Mie);
1269 }
1270
1271 if header.starts_with(b"<?xpacket") || header.starts_with(b"<x:xmpmeta") {
1273 return Some(FileType::Xmp);
1274 }
1275
1276 if header.starts_with(b"<?xml") || header.starts_with(b"<svg") {
1278 let preview = &header[..header.len().min(512)];
1279 if preview.windows(4).any(|w| w == b"<svg") {
1280 return Some(FileType::Svg);
1281 }
1282 if preview.windows(5).any(|w| w == b"<html" || w == b"<HTML") {
1283 return Some(FileType::Html); }
1285 if preview.windows(10).any(|w| w == b"<x:xmpmeta") || preview.windows(9).any(|w| w == b"<?xpacket") {
1286 return Some(FileType::Xmp);
1287 }
1288 if preview.windows(4).any(|w| w == b"<rdf" || w == b"<RDF") {
1289 return Some(FileType::Xmp);
1290 }
1291 if preview.windows(7).any(|w| w == b"<plist") || preview.windows(20).any(|w| w == b"DTD PLIST") {
1293 return Some(FileType::Plist);
1294 }
1295 return Some(FileType::Xmp);
1297 }
1298
1299 if header.starts_with(b"<!DOCTYPE html") || header.starts_with(b"<!doctype html")
1301 || header.starts_with(b"<!DOCTYPE HTML") || header.starts_with(b"<html") || header.starts_with(b"<HTML")
1302 {
1303 return Some(FileType::Html);
1304 }
1305
1306 if header.starts_with(&[0x7F, b'E', b'L', b'F']) {
1308 return Some(FileType::Exe);
1309 }
1310
1311 if header.starts_with(&[0xFE, 0xED, 0xFA, 0xCE])
1313 || header.starts_with(&[0xCE, 0xFA, 0xED, 0xFE])
1314 {
1315 return Some(FileType::Exe);
1316 }
1317
1318 if header.starts_with(&[0xFE, 0xED, 0xFA, 0xCF])
1320 || header.starts_with(&[0xCF, 0xFA, 0xED, 0xFE])
1321 {
1322 return Some(FileType::Exe);
1323 }
1324
1325 if header.starts_with(&[0xCA, 0xFE, 0xBA, 0xBE]) {
1327 return Some(FileType::Exe);
1328 }
1329
1330 if header.starts_with(b"MZ") {
1332 return Some(FileType::Exe);
1333 }
1334
1335 if (header.starts_with(&[0x00, 0x01, 0x00, 0x00]) || header.starts_with(b"true") || header.starts_with(b"typ1"))
1337 && header.len() >= 12
1338 {
1339 return Some(FileType::Font);
1340 }
1341
1342 if header.starts_with(b"ttcf") {
1344 return Some(FileType::Font);
1345 }
1346
1347 if header.starts_with(b"OTTO") {
1349 return Some(FileType::Font);
1350 }
1351
1352 if header.starts_with(b"wOFF") {
1354 return Some(FileType::Font);
1355 }
1356
1357 if header.starts_with(b"wOF2") {
1359 return Some(FileType::Font);
1360 }
1361
1362 if header.len() >= 2 && header[0] == b'V' && header[1] == b'6' {
1364 return Some(FileType::Moi);
1365 }
1366
1367 if header.starts_with(b"PGF") {
1369 return Some(FileType::Pgf);
1370 }
1371
1372 if header.starts_with(b"XISF0100") {
1374 return Some(FileType::Xisf);
1375 }
1376
1377 if header.len() >= 27 && header.starts_with(b"Paint Shop Pro Image File\n\x1a") {
1379 return Some(FileType::Psp);
1380 }
1381
1382 if header.len() >= 12
1385 && header[8] == 0x00 && header[9] == 0x00
1386 && header[10] == 0x00 && header[11] == 0x7C
1387 {
1388 return Some(FileType::SonyPmp);
1389 }
1390
1391 None
1392}
1393
1394pub fn detect_from_extension(ext: &str) -> Option<FileType> {
1396 let ext_lower = ext.to_ascii_lowercase();
1397 let ext_lower = ext_lower.trim_start_matches('.');
1398
1399 for &ft in ALL_FILE_TYPES {
1400 for known_ext in ft.extensions() {
1401 if ext_lower == *known_ext {
1402 return Some(ft);
1403 }
1404 }
1405 }
1406
1407 None
1408}
1409
1410impl std::fmt::Display for FileType {
1411 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1412 write!(f, "{}", self.description())
1413 }
1414}
1415
1416impl std::fmt::Display for Support {
1417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1418 match self {
1419 Support::Read => write!(f, "R"),
1420 Support::ReadWrite => write!(f, "R/W"),
1421 Support::ReadWriteCreate => write!(f, "R/W/C"),
1422 }
1423 }
1424}
1425
1426#[cfg(test)]
1427mod tests {
1428 use super::*;
1429
1430 #[test]
1431 fn test_detect_jpeg() {
1432 let header = [0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, b'J', b'F', b'I', b'F'];
1433 assert_eq!(detect_from_magic(&header), Some(FileType::Jpeg));
1434 }
1435
1436 #[test]
1437 fn test_detect_png() {
1438 let header = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
1439 assert_eq!(detect_from_magic(&header), Some(FileType::Png));
1440 }
1441
1442 #[test]
1443 fn test_detect_tiff_le() {
1444 let header = [b'I', b'I', 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00];
1445 assert_eq!(detect_from_magic(&header), Some(FileType::Tiff));
1446 }
1447
1448 #[test]
1449 fn test_detect_tiff_be() {
1450 let header = [b'M', b'M', 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08];
1451 assert_eq!(detect_from_magic(&header), Some(FileType::Tiff));
1452 }
1453
1454 #[test]
1455 fn test_detect_cr2() {
1456 let header = [b'I', b'I', 0x2A, 0x00, 0x10, 0x00, 0x00, 0x00, b'C', b'R'];
1457 assert_eq!(detect_from_magic(&header), Some(FileType::Cr2));
1458 }
1459
1460 #[test]
1461 fn test_detect_pdf() {
1462 let header = b"%PDF-1.7 some more data here";
1463 assert_eq!(detect_from_magic(header), Some(FileType::Pdf));
1464 }
1465
1466 #[test]
1467 fn test_detect_webp() {
1468 let header = b"RIFF\x00\x00\x00\x00WEBP";
1469 assert_eq!(detect_from_magic(header), Some(FileType::WebP));
1470 }
1471
1472 #[test]
1473 fn test_detect_heif() {
1474 let mut header = [0u8; 16];
1475 header[4..8].copy_from_slice(b"ftyp");
1476 header[8..12].copy_from_slice(b"heic");
1477 assert_eq!(detect_from_magic(&header), Some(FileType::Heif));
1478 }
1479
1480 #[test]
1481 fn test_detect_avif() {
1482 let mut header = [0u8; 16];
1483 header[4..8].copy_from_slice(b"ftyp");
1484 header[8..12].copy_from_slice(b"avif");
1485 assert_eq!(detect_from_magic(&header), Some(FileType::Avif));
1486 }
1487
1488 #[test]
1489 fn test_detect_cr3() {
1490 let mut header = [0u8; 16];
1491 header[4..8].copy_from_slice(b"ftyp");
1492 header[8..12].copy_from_slice(b"crx ");
1493 assert_eq!(detect_from_magic(&header), Some(FileType::Cr3));
1494 }
1495
1496 #[test]
1497 fn test_detect_flac() {
1498 assert_eq!(detect_from_magic(b"fLaC\x00\x00"), Some(FileType::Flac));
1499 }
1500
1501 #[test]
1502 fn test_detect_ogg() {
1503 assert_eq!(detect_from_magic(b"OggS\x00\x02"), Some(FileType::Ogg));
1504 }
1505
1506 #[test]
1507 fn test_detect_mp3_id3() {
1508 assert_eq!(detect_from_magic(b"ID3\x04\x00"), Some(FileType::Mp3));
1509 }
1510
1511 #[test]
1512 fn test_detect_rar() {
1513 assert_eq!(detect_from_magic(b"Rar!\x1A\x07\x01\x00"), Some(FileType::Rar));
1514 }
1515
1516 #[test]
1517 fn test_detect_7z() {
1518 assert_eq!(detect_from_magic(&[0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C]), Some(FileType::SevenZ));
1519 }
1520
1521 #[test]
1522 fn test_detect_gzip() {
1523 assert_eq!(detect_from_magic(&[0x1F, 0x8B, 0x08, 0x00]), Some(FileType::Gzip));
1524 }
1525
1526 #[test]
1527 fn test_detect_raf() {
1528 assert_eq!(detect_from_magic(b"FUJIFILMCCD-RAW 0201"), Some(FileType::Raf));
1529 }
1530
1531 #[test]
1532 fn test_detect_psd() {
1533 assert_eq!(detect_from_magic(b"8BPS\x00\x01"), Some(FileType::Psd));
1534 }
1535
1536 #[test]
1537 fn test_detect_from_extension() {
1538 assert_eq!(detect_from_extension("jpg"), Some(FileType::Jpeg));
1539 assert_eq!(detect_from_extension(".JPEG"), Some(FileType::Jpeg));
1540 assert_eq!(detect_from_extension("cr2"), Some(FileType::Cr2));
1541 assert_eq!(detect_from_extension("cr3"), Some(FileType::Cr3));
1542 assert_eq!(detect_from_extension("nef"), Some(FileType::Nef));
1543 assert_eq!(detect_from_extension("arw"), Some(FileType::Arw));
1544 assert_eq!(detect_from_extension("dng"), Some(FileType::Dng));
1545 assert_eq!(detect_from_extension("raf"), Some(FileType::Raf));
1546 assert_eq!(detect_from_extension("mp4"), Some(FileType::Mp4));
1547 assert_eq!(detect_from_extension("mov"), Some(FileType::QuickTime));
1548 assert_eq!(detect_from_extension("flac"), Some(FileType::Flac));
1549 assert_eq!(detect_from_extension("docx"), Some(FileType::Docx));
1550 assert_eq!(detect_from_extension("xlsx"), Some(FileType::Xlsx));
1551 assert_eq!(detect_from_extension("3fr"), Some(FileType::ThreeFR));
1552 assert_eq!(detect_from_extension("xyz"), None);
1553 }
1554
1555 #[test]
1556 fn test_all_types_have_extensions() {
1557 for &ft in FileType::all() {
1558 assert!(!ft.extensions().is_empty(), "{:?} has no extensions", ft);
1559 }
1560 }
1561
1562 #[test]
1563 fn test_all_types_have_mime() {
1564 for &ft in FileType::all() {
1565 assert!(!ft.mime_type().is_empty(), "{:?} has no MIME type", ft);
1566 }
1567 }
1568
1569 #[test]
1570 fn test_total_format_count() {
1571 assert!(FileType::all().len() >= 100, "Expected 100+ formats, got {}", FileType::all().len());
1572 }
1573}