use std::{fmt::Display, str::FromStr}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tracing::debug; /// A media type, conveniently parsed #[derive(Debug, PartialEq, Eq, Clone)] pub enum MimeType { /// A mimetype we didn't recognize Other(String), /// An unstructured binary blob /// Use this whenever a mime type is unknown Blob, // MARK: Audio /// AAC audio file (audio/aac) Aac, /// FLAC audio file (audio/flac) Flac, /// MIDI audio file (audio/midi) Midi, /// MP3 audio file (audio/mpeg) Mp3, /// OGG audio file (audio/ogg) Oga, /// Opus audio file in Ogg container (audio/ogg) Opus, /// Waveform Audio Format (audio/wav) Wav, /// WEBM audio file (audio/webm) Weba, // MARK: Video /// AVI: Audio Video Interleave (video/x-msvideo) Avi, /// MP4 video file (video/mp4) Mp4, /// MPEG video file (video/mpeg) Mpeg, /// OGG video file (video/ogg) Ogv, /// MPEG transport stream (video/mp2t) Ts, /// WEBM video file (video/webm) WebmVideo, /// 3GPP audio/video container (video/3gpp) ThreeGp, /// 3GPP2 audio/video container (video/3gpp2) ThreeG2, // MARK: Images /// Animated Portable Network Graphics (image/apng) Apng, /// AVIF image (image/avif) Avif, /// Windows OS/2 Bitmap Graphics (image/bmp) Bmp, /// Graphics Interchange Format (image/gif) Gif, /// Icon format (image/vnd.microsoft.icon) Ico, /// JPEG image (image/jpeg) Jpg, /// Portable Network Graphics (image/png) Png, /// Scalable Vector Graphics (image/svg+xml) Svg, /// Tagged Image File Format (image/tiff) Tiff, /// WEBP image (image/webp) Webp, // MARK: Text /// Plain text (text/plain) Text, /// Cascading Style Sheets (text/css) Css, /// Comma-separated values (text/csv) Csv, /// HyperText Markup Language (text/html) Html, /// JavaScript (text/javascript) Javascript, /// JSON format (application/json) Json, /// JSON-LD format (application/ld+json) JsonLd, /// XML (application/xml) Xml, // MARK: Documents /// Adobe Portable Document Format (application/pdf) Pdf, /// Rich Text Format (application/rtf) Rtf, // MARK: Archives /// Archive document, multiple files embedded (application/x-freearc) Arc, /// BZip archive (application/x-bzip) Bz, /// BZip2 archive (application/x-bzip2) Bz2, /// GZip Compressed Archive (application/gzip) Gz, /// Java Archive (application/java-archive) Jar, /// OGG (application/ogg) Ogg, /// RAR archive (application/vnd.rar) Rar, /// 7-zip archive (application/x-7z-compressed) SevenZ, /// Tape Archive (application/x-tar) Tar, /// ZIP archive (application/zip) Zip, // MARK: Fonts /// MS Embedded OpenType fonts (application/vnd.ms-fontobject) Eot, /// OpenType font (font/otf) Otf, /// TrueType Font (font/ttf) Ttf, /// Web Open Font Format (font/woff) Woff, /// Web Open Font Format 2 (font/woff2) Woff2, // MARK: Applications /// AbiWord document (application/x-abiword) Abiword, /// Amazon Kindle eBook format (application/vnd.amazon.ebook) Azw, /// CD audio (application/x-cdf) Cda, /// C-Shell script (application/x-csh) Csh, /// Microsoft Word (application/msword) Doc, /// Microsoft Word OpenXML (application/vnd.openxmlformats-officedocument.wordprocessingml.document) Docx, /// Electronic publication (application/epub+zip) Epub, /// iCalendar format (text/calendar) Ics, /// Apple Installer Package (application/vnd.apple.installer+xml) Mpkg, /// OpenDocument presentation (application/vnd.oasis.opendocument.presentation) Odp, /// OpenDocument spreadsheet (application/vnd.oasis.opendocument.spreadsheet) Ods, /// OpenDocument text document (application/vnd.oasis.opendocument.text) Odt, /// Hypertext Preprocessor (application/x-httpd-php) Php, /// Microsoft PowerPoint (application/vnd.ms-powerpoint) Ppt, /// Microsoft PowerPoint OpenXML (application/vnd.openxmlformats-officedocument.presentationml.presentation) Pptx, /// Bourne shell script (application/x-sh) Sh, /// Microsoft Visio (application/vnd.visio) Vsd, /// XHTML (application/xhtml+xml) Xhtml, /// Microsoft Excel (application/vnd.ms-excel) Xls, /// Microsoft Excel OpenXML (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet) Xlsx, /// XUL (application/vnd.mozilla.xul+xml) Xul, } // MARK: ser/de /* impl utoipa::ToSchema for MimeType { fn name() -> std::borrow::Cow<'static, str> { std::borrow::Cow::Borrowed("MimeType") } } impl utoipa::PartialSchema for MimeType { fn schema() -> utoipa::openapi::RefOr { utoipa::openapi::Schema::Object( utoipa::openapi::schema::ObjectBuilder::new() .schema_type(utoipa::openapi::schema::SchemaType::Type(Type::String)) .description(Some( "A media type string (e.g., 'application/json', 'text/plain')", )) .examples(Some("application/json")) .build(), ) .into() } } */ impl Serialize for MimeType { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.to_string()) } } impl<'de> Deserialize<'de> for MimeType { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; Ok(MimeType::from_str(&s).unwrap()) } } // MARK: misc impl Default for MimeType { fn default() -> Self { Self::Blob } } impl From for MimeType { fn from(value: String) -> Self { Self::from_str(&value).unwrap() } } impl From<&str> for MimeType { fn from(value: &str) -> Self { Self::from_str(value).unwrap() } } impl From<&MimeType> for String { fn from(value: &MimeType) -> Self { value.to_string() } } impl FromStr for MimeType { type Err = std::convert::Infallible; // Must match `display` below, but may provide other alternatives. fn from_str(s: &str) -> Result { Ok(match s { "application/octet-stream" => Self::Blob, // MARK: Audio "audio/aac" => Self::Aac, "audio/flac" => Self::Flac, "audio/midi" | "audio/x-midi" => Self::Midi, "audio/mpeg" => Self::Mp3, "audio/ogg" => Self::Oga, "audio/wav" => Self::Wav, "audio/webm" => Self::Weba, // MARK: Video "video/x-msvideo" => Self::Avi, "video/mp4" => Self::Mp4, "video/mpeg" => Self::Mpeg, "video/ogg" => Self::Ogv, "video/mp2t" => Self::Ts, "video/webm" => Self::WebmVideo, "video/3gpp" => Self::ThreeGp, "video/3gpp2" => Self::ThreeG2, // MARK: Images "image/apng" => Self::Apng, "image/avif" => Self::Avif, "image/bmp" => Self::Bmp, "image/gif" => Self::Gif, "image/vnd.microsoft.icon" => Self::Ico, "image/jpeg" | "image/jpg" => Self::Jpg, "image/png" => Self::Png, "image/svg+xml" => Self::Svg, "image/tiff" => Self::Tiff, "image/webp" => Self::Webp, // MARK: Text "text/plain" => Self::Text, "text/css" => Self::Css, "text/csv" => Self::Csv, "text/html" => Self::Html, "text/javascript" => Self::Javascript, "application/json" => Self::Json, "application/ld+json" => Self::JsonLd, "application/xml" | "text/xml" => Self::Xml, // MARK: Documents "application/pdf" => Self::Pdf, "application/rtf" => Self::Rtf, // MARK: Archives "application/x-freearc" => Self::Arc, "application/x-bzip" => Self::Bz, "application/x-bzip2" => Self::Bz2, "application/gzip" | "application/x-gzip" => Self::Gz, "application/java-archive" => Self::Jar, "application/ogg" => Self::Ogg, "application/vnd.rar" => Self::Rar, "application/x-7z-compressed" => Self::SevenZ, "application/x-tar" => Self::Tar, "application/zip" | "application/x-zip-compressed" => Self::Zip, // MARK: Fonts "application/vnd.ms-fontobject" => Self::Eot, "font/otf" => Self::Otf, "font/ttf" => Self::Ttf, "font/woff" => Self::Woff, "font/woff2" => Self::Woff2, // MARK: Applications "application/x-abiword" => Self::Abiword, "application/vnd.amazon.ebook" => Self::Azw, "application/x-cdf" => Self::Cda, "application/x-csh" => Self::Csh, "application/msword" => Self::Doc, "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => Self::Docx, "application/epub+zip" => Self::Epub, "text/calendar" => Self::Ics, "application/vnd.apple.installer+xml" => Self::Mpkg, "application/vnd.oasis.opendocument.presentation" => Self::Odp, "application/vnd.oasis.opendocument.spreadsheet" => Self::Ods, "application/vnd.oasis.opendocument.text" => Self::Odt, "application/x-httpd-php" => Self::Php, "application/vnd.ms-powerpoint" => Self::Ppt, "application/vnd.openxmlformats-officedocument.presentationml.presentation" => { Self::Pptx } "application/x-sh" => Self::Sh, "application/vnd.visio" => Self::Vsd, "application/xhtml+xml" => Self::Xhtml, "application/vnd.ms-excel" => Self::Xls, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => Self::Xlsx, "application/vnd.mozilla.xul+xml" => Self::Xul, _ => { debug!(message = "Encountered unknown mimetype", mime_string = s); Self::Other(s.into()) } }) } } impl Display for MimeType { /// Get a string representation of this mimetype. /// /// The following always holds: /// ```rust /// # use toolbox::mime::MimeType; /// # let x = MimeType::Blob; /// assert_eq!(MimeType::from(x.to_string()), x); /// ``` /// /// The following might not hold: /// ```rust /// # use toolbox::mime::MimeType; /// # let y = "application/custom"; /// // MimeType::from(y).to_string() may not equal y /// ``` fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Blob => write!(f, "application/octet-stream"), // MARK: Audio Self::Aac => write!(f, "audio/aac"), Self::Flac => write!(f, "audio/flac"), Self::Midi => write!(f, "audio/midi"), Self::Mp3 => write!(f, "audio/mpeg"), Self::Oga => write!(f, "audio/ogg"), Self::Opus => write!(f, "audio/ogg"), Self::Wav => write!(f, "audio/wav"), Self::Weba => write!(f, "audio/webm"), // MARK: Video Self::Avi => write!(f, "video/x-msvideo"), Self::Mp4 => write!(f, "video/mp4"), Self::Mpeg => write!(f, "video/mpeg"), Self::Ogv => write!(f, "video/ogg"), Self::Ts => write!(f, "video/mp2t"), Self::WebmVideo => write!(f, "video/webm"), Self::ThreeGp => write!(f, "video/3gpp"), Self::ThreeG2 => write!(f, "video/3gpp2"), // MARK: Images Self::Apng => write!(f, "image/apng"), Self::Avif => write!(f, "image/avif"), Self::Bmp => write!(f, "image/bmp"), Self::Gif => write!(f, "image/gif"), Self::Ico => write!(f, "image/vnd.microsoft.icon"), Self::Jpg => write!(f, "image/jpeg"), Self::Png => write!(f, "image/png"), Self::Svg => write!(f, "image/svg+xml"), Self::Tiff => write!(f, "image/tiff"), Self::Webp => write!(f, "image/webp"), // MARK: Text Self::Text => write!(f, "text/plain"), Self::Css => write!(f, "text/css"), Self::Csv => write!(f, "text/csv"), Self::Html => write!(f, "text/html"), Self::Javascript => write!(f, "text/javascript"), Self::Json => write!(f, "application/json"), Self::JsonLd => write!(f, "application/ld+json"), Self::Xml => write!(f, "application/xml"), // MARK: Documents Self::Pdf => write!(f, "application/pdf"), Self::Rtf => write!(f, "application/rtf"), // MARK: Archives Self::Arc => write!(f, "application/x-freearc"), Self::Bz => write!(f, "application/x-bzip"), Self::Bz2 => write!(f, "application/x-bzip2"), Self::Gz => write!(f, "application/gzip"), Self::Jar => write!(f, "application/java-archive"), Self::Ogg => write!(f, "application/ogg"), Self::Rar => write!(f, "application/vnd.rar"), Self::SevenZ => write!(f, "application/x-7z-compressed"), Self::Tar => write!(f, "application/x-tar"), Self::Zip => write!(f, "application/zip"), // MARK: Fonts Self::Eot => write!(f, "application/vnd.ms-fontobject"), Self::Otf => write!(f, "font/otf"), Self::Ttf => write!(f, "font/ttf"), Self::Woff => write!(f, "font/woff"), Self::Woff2 => write!(f, "font/woff2"), // MARK: Applications Self::Abiword => write!(f, "application/x-abiword"), Self::Azw => write!(f, "application/vnd.amazon.ebook"), Self::Cda => write!(f, "application/x-cdf"), Self::Csh => write!(f, "application/x-csh"), Self::Doc => write!(f, "application/msword"), Self::Docx => write!( f, "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ), Self::Epub => write!(f, "application/epub+zip"), Self::Ics => write!(f, "text/calendar"), Self::Mpkg => write!(f, "application/vnd.apple.installer+xml"), Self::Odp => write!(f, "application/vnd.oasis.opendocument.presentation"), Self::Ods => write!(f, "application/vnd.oasis.opendocument.spreadsheet"), Self::Odt => write!(f, "application/vnd.oasis.opendocument.text"), Self::Php => write!(f, "application/x-httpd-php"), Self::Ppt => write!(f, "application/vnd.ms-powerpoint"), Self::Pptx => write!( f, "application/vnd.openxmlformats-officedocument.presentationml.presentation" ), Self::Sh => write!(f, "application/x-sh"), Self::Vsd => write!(f, "application/vnd.visio"), Self::Xhtml => write!(f, "application/xhtml+xml"), Self::Xls => write!(f, "application/vnd.ms-excel"), Self::Xlsx => write!( f, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ), Self::Xul => write!(f, "application/vnd.mozilla.xul+xml"), Self::Other(x) => write!(f, "{x}"), } } } impl MimeType { // Must match `From` above /// Try to guess a file's mime type from its extension. /// `ext` should NOT start with a dot. pub fn from_extension(ext: &str) -> Option { Some(match ext { // MARK: Audio "aac" => Self::Aac, "flac" => Self::Flac, "mid" | "midi" => Self::Midi, "mp3" => Self::Mp3, "oga" => Self::Oga, "opus" => Self::Opus, "wav" => Self::Wav, "weba" => Self::Weba, // MARK: Video "avi" => Self::Avi, "mp4" => Self::Mp4, "mpeg" => Self::Mpeg, "ogv" => Self::Ogv, "ts" => Self::Ts, "webm" => Self::WebmVideo, "3gp" => Self::ThreeGp, "3g2" => Self::ThreeG2, // MARK: Images "apng" => Self::Apng, "avif" => Self::Avif, "bmp" => Self::Bmp, "gif" => Self::Gif, "ico" => Self::Ico, "jpg" | "jpeg" => Self::Jpg, "png" => Self::Png, "svg" => Self::Svg, "tif" | "tiff" => Self::Tiff, "webp" => Self::Webp, // MARK: Text "txt" => Self::Text, "css" => Self::Css, "csv" => Self::Csv, "htm" | "html" => Self::Html, "js" | "mjs" => Self::Javascript, "json" => Self::Json, "jsonld" => Self::JsonLd, "xml" => Self::Xml, // MARK: Documents "pdf" => Self::Pdf, "rtf" => Self::Rtf, // MARK: Archives "arc" => Self::Arc, "bz" => Self::Bz, "bz2" => Self::Bz2, "gz" => Self::Gz, "jar" => Self::Jar, "ogx" => Self::Ogg, "rar" => Self::Rar, "7z" => Self::SevenZ, "tar" => Self::Tar, "zip" => Self::Zip, // MARK: Fonts "eot" => Self::Eot, "otf" => Self::Otf, "ttf" => Self::Ttf, "woff" => Self::Woff, "woff2" => Self::Woff2, // MARK: Applications "abw" => Self::Abiword, "azw" => Self::Azw, "cda" => Self::Cda, "csh" => Self::Csh, "doc" => Self::Doc, "docx" => Self::Docx, "epub" => Self::Epub, "ics" => Self::Ics, "mpkg" => Self::Mpkg, "odp" => Self::Odp, "ods" => Self::Ods, "odt" => Self::Odt, "php" => Self::Php, "ppt" => Self::Ppt, "pptx" => Self::Pptx, "sh" => Self::Sh, "vsd" => Self::Vsd, "xhtml" => Self::Xhtml, "xls" => Self::Xls, "xlsx" => Self::Xlsx, "xul" => Self::Xul, _ => return None, }) } /// Get the extension we use for files with this type. /// Includes a dot. Might be the empty string. pub fn extension(&self) -> &str { match self { Self::Blob => "", Self::Other(_) => "", // MARK: Audio Self::Aac => ".aac", Self::Flac => ".flac", Self::Midi => ".midi", Self::Mp3 => ".mp3", Self::Oga => ".oga", Self::Opus => ".opus", Self::Wav => ".wav", Self::Weba => ".weba", // MARK: Video Self::Avi => ".avi", Self::Mp4 => ".mp4", Self::Mpeg => ".mpeg", Self::Ogv => ".ogv", Self::Ts => ".ts", Self::WebmVideo => ".webm", Self::ThreeGp => ".3gp", Self::ThreeG2 => ".3g2", // MARK: Images Self::Apng => ".apng", Self::Avif => ".avif", Self::Bmp => ".bmp", Self::Gif => ".gif", Self::Ico => ".ico", Self::Jpg => ".jpg", Self::Png => ".png", Self::Svg => ".svg", Self::Tiff => ".tiff", Self::Webp => ".webp", // MARK: Text Self::Text => ".txt", Self::Css => ".css", Self::Csv => ".csv", Self::Html => ".html", Self::Javascript => ".js", Self::Json => ".json", Self::JsonLd => ".jsonld", Self::Xml => ".xml", // MARK: Documents Self::Pdf => ".pdf", Self::Rtf => ".rtf", // MARK: Archives Self::Arc => ".arc", Self::Bz => ".bz", Self::Bz2 => ".bz2", Self::Gz => ".gz", Self::Jar => ".jar", Self::Ogg => ".ogx", Self::Rar => ".rar", Self::SevenZ => ".7z", Self::Tar => ".tar", Self::Zip => ".zip", // MARK: Fonts Self::Eot => ".eot", Self::Otf => ".otf", Self::Ttf => ".ttf", Self::Woff => ".woff", Self::Woff2 => ".woff2", // MARK: Applications Self::Abiword => ".abw", Self::Azw => ".azw", Self::Cda => ".cda", Self::Csh => ".csh", Self::Doc => ".doc", Self::Docx => ".docx", Self::Epub => ".epub", Self::Ics => ".ics", Self::Mpkg => ".mpkg", Self::Odp => ".odp", Self::Ods => ".ods", Self::Odt => ".odt", Self::Php => ".php", Self::Ppt => ".ppt", Self::Pptx => ".pptx", Self::Sh => ".sh", Self::Vsd => ".vsd", Self::Xhtml => ".xhtml", Self::Xls => ".xls", Self::Xlsx => ".xlsx", Self::Xul => ".xul", } } }