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