diff --git a/Cargo.lock b/Cargo.lock index 23ab347..1ff9568 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -307,6 +307,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.8.8" @@ -443,6 +453,7 @@ dependencies = [ "display_full_error", "flate2", "glob", + "mime_guess", "proc-macro2", "quote", "sha1", @@ -542,6 +553,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-ident" version = "1.0.18" diff --git a/static-serve-macro/Cargo.toml b/static-serve-macro/Cargo.toml index 29e0c64..f85daa0 100644 --- a/static-serve-macro/Cargo.toml +++ b/static-serve-macro/Cargo.toml @@ -16,6 +16,7 @@ proc-macro = true display_full_error = "1.1" flate2 = "1.1" glob = "0.3" +mime_guess = "2.0.5" proc-macro2 = "1.0" quote = "1.0" sha1 = "0.10" diff --git a/static-serve-macro/src/error.rs b/static-serve-macro/src/error.rs index 7230f8a..d3764e2 100644 --- a/static-serve-macro/src/error.rs +++ b/static-serve-macro/src/error.rs @@ -11,6 +11,8 @@ use thiserror::Error; pub(crate) enum Error { #[error("{}", UnknownFileExtension(.0.as_deref()))] UnknownFileExtension(Option), + #[error("File extension for file {} is not valid unicode", 0.to_string())] + InvalidFileExtension(OsString), #[error("Cannot canonicalize assets directory")] CannotCanonicalizeDirectory(#[source] io::Error), #[error("Invalid unicode in directory name")] diff --git a/static-serve-macro/src/lib.rs b/static-serve-macro/src/lib.rs index bafc025..a8607ae 100644 --- a/static-serve-macro/src/lib.rs +++ b/static-serve-macro/src/lib.rs @@ -357,15 +357,28 @@ fn maybe_get_compressed(compressed: &[u8], contents: &[u8]) -> Option Result<&'static str, error::Error> { +/// Use `mime_guess` to get the best guess of the file's MIME type +/// by looking at its extension, or return an error if unable. +/// +/// We accept the first guess because [`mime_guess` updates the order +/// according to the latest IETF RTC](https://docs.rs/mime_guess/2.0.5/mime_guess/struct.MimeGuess.html#note-ordering) +fn file_content_type(path: &Path) -> Result { match path.extension() { - Some(ext) if ext.eq_ignore_ascii_case("css") => Ok("text/css"), - Some(ext) if ext.eq_ignore_ascii_case("js") => Ok("text/javascript"), - Some(ext) if ext.eq_ignore_ascii_case("txt") => Ok("text/plain"), - Some(ext) if ext.eq_ignore_ascii_case("woff") => Ok("font/woff"), - Some(ext) if ext.eq_ignore_ascii_case("woff2") => Ok("font/woff2"), - Some(ext) if ext.eq_ignore_ascii_case("svg") => Ok("image/svg+xml"), - ext => Err(error::Error::UnknownFileExtension(ext.map(Into::into))), + Some(ext) => { + let guesses = mime_guess::MimeGuess::from_ext( + ext.to_str() + .ok_or(error::Error::InvalidFileExtension(path.into()))?, + ); + + if let Some(guess) = guesses.first_raw() { + Ok(guess.to_owned()) + } else { + Err(error::Error::UnknownFileExtension( + path.extension().map(Into::into), + )) + } + } + None => Err(error::Error::UnknownFileExtension(None)), } }