1use std::fmt;
8
9const MIMETYPE_PLAIN: &str = "text/plain";
10
11#[allow(missing_docs)]
13pub enum MimeType {
14 Css,
15 Csv,
16 Html,
17 Ico,
18 Js,
19 Json,
20 Jsonld,
21 Mp4,
22 OctetStream,
23 Rtf,
24 Svg,
25 Txt,
26}
27
28impl std::fmt::Display for MimeType {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 let mime = match self {
31 MimeType::Css => "text/css",
32 MimeType::Csv => "text/csv",
33 MimeType::Html => "text/html",
34 MimeType::Ico => "image/vnd.microsoft.icon",
35 MimeType::Js => "text/javascript",
36 MimeType::Json => "application/json",
37 MimeType::Jsonld => "application/ld+json",
38 MimeType::Mp4 => "video/mp4",
39 MimeType::OctetStream => "application/octet-stream",
40 MimeType::Rtf => "application/rtf",
41 MimeType::Svg => "image/svg+xml",
42 MimeType::Txt => MIMETYPE_PLAIN,
43 };
44 write!(f, "{mime}")
45 }
46}
47
48impl MimeType {
49 pub fn parse_from_uri(uri: &str) -> MimeType {
51 Self::parse_from_uri_with_fallback(uri, Self::Html)
52 }
53
54 pub fn parse_from_uri_with_fallback(uri: &str, fallback: MimeType) -> MimeType {
56 let suffix = uri.split('.').last();
57 match suffix {
58 Some("bin") => Self::OctetStream,
59 Some("css" | "less" | "sass" | "styl") => Self::Css,
60 Some("csv") => Self::Csv,
61 Some("html") => Self::Html,
62 Some("ico") => Self::Ico,
63 Some("js") => Self::Js,
64 Some("json") => Self::Json,
65 Some("jsonld") => Self::Jsonld,
66 Some("mjs") => Self::Js,
67 Some("mp4") => Self::Mp4,
68 Some("rtf") => Self::Rtf,
69 Some("svg") => Self::Svg,
70 Some("txt") => Self::Txt,
71 Some(_) => fallback,
73 None => Self::OctetStream,
76 }
77 }
78
79 pub fn parse(content: &[u8], uri: &str) -> String {
81 Self::parse_with_fallback(content, uri, Self::Html)
82 }
83 pub fn parse_with_fallback(content: &[u8], uri: &str, fallback: MimeType) -> String {
85 let mime = if uri.ends_with(".svg") {
86 None
88 } else {
89 infer::get(content).map(|info| info.mime_type())
90 };
91
92 match mime {
93 Some(mime) if mime == MIMETYPE_PLAIN => {
94 Self::parse_from_uri_with_fallback(uri, fallback).to_string()
95 }
96 None => Self::parse_from_uri_with_fallback(uri, fallback).to_string(),
97 Some(mime) => mime.to_string(),
98 }
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn should_parse_mimetype_from_uri() {
108 let css = MimeType::parse_from_uri(
109 "https://unpkg.com/browse/bootstrap@4.1.0/dist/css/bootstrap-grid.css",
110 )
111 .to_string();
112 assert_eq!(css, "text/css".to_string());
113
114 let csv: String = MimeType::parse_from_uri("https://example.com/random.csv").to_string();
115 assert_eq!(csv, "text/csv".to_string());
116
117 let ico: String =
118 MimeType::parse_from_uri("https://icons.duckduckgo.com/ip3/microsoft.com.ico").to_string();
119 assert_eq!(ico, String::from("image/vnd.microsoft.icon"));
120
121 let html: String = MimeType::parse_from_uri("https://tauri.app/index.html").to_string();
122 assert_eq!(html, String::from("text/html"));
123
124 let js: String =
125 MimeType::parse_from_uri("https://unpkg.com/react@17.0.1/umd/react.production.min.js")
126 .to_string();
127 assert_eq!(js, "text/javascript".to_string());
128
129 let json: String =
130 MimeType::parse_from_uri("https://unpkg.com/browse/react@17.0.1/build-info.json").to_string();
131 assert_eq!(json, String::from("application/json"));
132
133 let jsonld: String = MimeType::parse_from_uri("https:/example.com/hello.jsonld").to_string();
134 assert_eq!(jsonld, String::from("application/ld+json"));
135
136 let mjs: String = MimeType::parse_from_uri("https://example.com/bundled.mjs").to_string();
137 assert_eq!(mjs, String::from("text/javascript"));
138
139 let mp4: String = MimeType::parse_from_uri("https://example.com/video.mp4").to_string();
140 assert_eq!(mp4, String::from("video/mp4"));
141
142 let rtf: String = MimeType::parse_from_uri("https://example.com/document.rtf").to_string();
143 assert_eq!(rtf, String::from("application/rtf"));
144
145 let svg: String = MimeType::parse_from_uri("https://example.com/picture.svg").to_string();
146 assert_eq!(svg, String::from("image/svg+xml"));
147
148 let txt: String = MimeType::parse_from_uri("https://example.com/file.txt").to_string();
149 assert_eq!(txt, String::from("text/plain"));
150
151 let custom_scheme = MimeType::parse_from_uri("wry://tauri.app").to_string();
152 assert_eq!(custom_scheme, String::from("text/html"));
153 }
154}