Skip to content

Commit 0b0c7c6

Browse files
committed
use mime::Mime for mime types / content types
1 parent 130cfac commit 0b0c7c6

File tree

8 files changed

+73
-49
lines changed

8 files changed

+73
-49
lines changed

src/db/file.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
//! However, postgres is still available for testing and backwards compatibility.
99
1010
use crate::error::Result;
11-
use crate::storage::{AsyncStorage, CompressionAlgorithm};
11+
use crate::{
12+
db::mimes,
13+
storage::{AsyncStorage, CompressionAlgorithm},
14+
};
1215
use mime::Mime;
1316
use serde_json::Value;
1417
use std::ffi::OsStr;
@@ -25,28 +28,30 @@ pub struct FileEntry {
2528

2629
impl FileEntry {
2730
pub(crate) fn mime(&self) -> Mime {
28-
detect_mime(&self.path).parse().unwrap()
31+
detect_mime(&self.path)
2932
}
3033
}
3134

32-
pub(crate) fn detect_mime(file_path: impl AsRef<Path>) -> &'static str {
35+
pub(crate) fn detect_mime(file_path: impl AsRef<Path>) -> Mime {
3336
let mime = mime_guess::from_path(file_path.as_ref())
34-
.first_raw()
35-
.unwrap_or("text/plain");
36-
match mime {
37+
.first()
38+
.unwrap_or(mime::TEXT_PLAIN);
39+
40+
match mime.as_ref() {
3741
"text/plain" | "text/troff" | "text/x-markdown" | "text/x-rust" | "text/x-toml" => {
3842
match file_path.as_ref().extension().and_then(OsStr::to_str) {
39-
Some("md") => "text/markdown",
40-
Some("rs") => "text/rust",
41-
Some("markdown") => "text/markdown",
42-
Some("css") => "text/css",
43-
Some("toml") => "text/toml",
44-
Some("js") => "application/javascript",
45-
Some("json") => "application/json",
43+
Some("md") => mimes::TEXT_MARKDOWN.clone(),
44+
Some("rs") => mimes::TEXT_RUST.clone(),
45+
Some("markdown") => mimes::TEXT_MARKDOWN.clone(),
46+
Some("css") => mime::TEXT_CSS,
47+
Some("toml") => mimes::TEXT_TOML.clone(),
48+
Some("js") => mime::TEXT_JAVASCRIPT,
49+
Some("json") => mime::APPLICATION_JSON,
4650
_ => mime,
4751
}
4852
}
49-
"image/svg" => "image/svg+xml",
53+
"image/svg" => mime::IMAGE_SVG,
54+
5055
_ => mime,
5156
}
5257
}

src/db/mimes.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use mime::Mime;
2+
use once_cell::sync::Lazy;
3+
4+
macro_rules! mime {
5+
($id:ident, $mime:expr) => {
6+
pub(crate) static $id: Lazy<Mime> = Lazy::new(|| $mime.parse().unwrap());
7+
};
8+
}
9+
10+
mime!(APPLICATION_ZIP, "application/zip");
11+
mime!(TEXT_MARKDOWN, "text/markdown");
12+
mime!(TEXT_RUST, "text/rust");
13+
mime!(TEXT_TOML, "text/toml");

src/db/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod add_package;
1919
pub mod blacklist;
2020
pub mod delete;
2121
pub(crate) mod file;
22+
pub(crate) mod mimes;
2223
mod overrides;
2324
mod pool;
2425
pub(crate) mod types;

src/storage/database.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,10 @@ impl DatabaseBackend {
136136
});
137137
Ok(Blob {
138138
path: result.path,
139-
mime: result.mime,
139+
mime: result
140+
.mime
141+
.parse()
142+
.unwrap_or(mime::APPLICATION_OCTET_STREAM),
140143
date_updated: result.date_updated,
141144
content: result.content.unwrap_or_default(),
142145
compression,
@@ -154,7 +157,7 @@ impl DatabaseBackend {
154157
ON CONFLICT (path) DO UPDATE
155158
SET mime = EXCLUDED.mime, content = EXCLUDED.content, compression = EXCLUDED.compression",
156159
&blob.path,
157-
&blob.mime,
160+
&blob.mime.to_string(),
158161
&blob.content,
159162
compression,
160163
)

src/storage/mod.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use self::s3::S3Backend;
99
use crate::{
1010
db::{
1111
file::{detect_mime, FileEntry},
12-
Pool,
12+
mimes, Pool,
1313
},
1414
error::Result,
1515
utils::spawn_blocking,
@@ -19,6 +19,7 @@ use anyhow::anyhow;
1919
use chrono::{DateTime, Utc};
2020
use fn_error_context::context;
2121
use futures_util::stream::BoxStream;
22+
use mime::Mime;
2223
use path_slash::PathExt;
2324
use std::iter;
2425
use std::{
@@ -41,7 +42,7 @@ pub(crate) struct PathNotFoundError;
4142
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4243
pub(crate) struct Blob {
4344
pub(crate) path: String,
44-
pub(crate) mime: String,
45+
pub(crate) mime: Mime,
4546
pub(crate) date_updated: DateTime<Utc>,
4647
pub(crate) content: Vec<u8>,
4748
pub(crate) compression: Option<CompressionAlgorithm>,
@@ -378,7 +379,7 @@ impl AsyncStorage {
378379

379380
Ok(Blob {
380381
path: format!("{archive_path}/{path}"),
381-
mime: detect_mime(path).into(),
382+
mime: detect_mime(path),
382383
date_updated: blob.date_updated,
383384
content: blob.content,
384385
compression: None,
@@ -460,14 +461,14 @@ impl AsyncStorage {
460461
self.store_inner(vec![
461462
Blob {
462463
path: archive_path.to_string(),
463-
mime: "application/zip".to_owned(),
464+
mime: mimes::APPLICATION_ZIP.clone(),
464465
content: zip_content,
465466
compression: None,
466467
date_updated: Utc::now(),
467468
},
468469
Blob {
469470
path: remote_index_path,
470-
mime: "application/octet-stream".to_owned(),
471+
mime: mime::APPLICATION_OCTET_STREAM,
471472
content: compressed_index_content,
472473
compression: Some(alg),
473474
date_updated: Utc::now(),
@@ -512,7 +513,7 @@ impl AsyncStorage {
512513
path: file_path,
513514
size: file_size,
514515
};
515-
let mime = file_info.mime().to_string();
516+
let mime = file_info.mime();
516517
file_paths.push(file_info);
517518

518519
blobs.push(Blob {
@@ -877,7 +878,7 @@ mod backend_tests {
877878
assert!(!storage.exists("path/to/file.txt").unwrap());
878879
let blob = Blob {
879880
path: "path/to/file.txt".into(),
880-
mime: "text/plain".into(),
881+
mime: mime::TEXT_PLAIN,
881882
date_updated: Utc::now(),
882883
content: "Hello world!".into(),
883884
compression: None,
@@ -893,7 +894,7 @@ mod backend_tests {
893894

894895
storage.store_blobs(vec![Blob {
895896
path: path.into(),
896-
mime: "text/plain".into(),
897+
mime: mime::TEXT_PLAIN,
897898
date_updated: Utc::now(),
898899
compression: None,
899900
content: b"test content\n".to_vec(),
@@ -920,7 +921,7 @@ mod backend_tests {
920921
let path: &str = "foo/bar.txt";
921922
let blob = Blob {
922923
path: path.into(),
923-
mime: "text/plain".into(),
924+
mime: mime::TEXT_PLAIN,
924925
date_updated: Utc::now(),
925926
compression: None,
926927
content: b"test content\n".to_vec(),
@@ -955,7 +956,7 @@ mod backend_tests {
955956
fn test_get_range(storage: &Storage) -> Result<()> {
956957
let blob = Blob {
957958
path: "foo/bar.txt".into(),
958-
mime: "text/plain".into(),
959+
mime: mime::TEXT_PLAIN,
959960
date_updated: Utc::now(),
960961
compression: None,
961962
content: b"test content\n".to_vec(),
@@ -995,7 +996,7 @@ mod backend_tests {
995996
.iter()
996997
.map(|&filename| Blob {
997998
path: filename.into(),
998-
mime: "text/plain".into(),
999+
mime: mime::TEXT_PLAIN,
9991000
date_updated: Utc::now(),
10001001
compression: None,
10011002
content: b"test content\n".to_vec(),
@@ -1036,14 +1037,14 @@ mod backend_tests {
10361037

10371038
let small_blob = Blob {
10381039
path: "small-blob.bin".into(),
1039-
mime: "text/plain".into(),
1040+
mime: mime::TEXT_PLAIN,
10401041
date_updated: Utc::now(),
10411042
content: vec![0; MAX_SIZE],
10421043
compression: None,
10431044
};
10441045
let big_blob = Blob {
10451046
path: "big-blob.bin".into(),
1046-
mime: "text/plain".into(),
1047+
mime: mime::TEXT_PLAIN,
10471048
date_updated: Utc::now(),
10481049
content: vec![0; MAX_SIZE * 2],
10491050
compression: None,
@@ -1078,7 +1079,7 @@ mod backend_tests {
10781079
.iter()
10791080
.map(|&path| Blob {
10801081
path: path.into(),
1081-
mime: "text/plain".into(),
1082+
mime: mime::TEXT_PLAIN,
10821083
date_updated: Utc::now(),
10831084
compression: None,
10841085
content: b"Hello world!\n".to_vec(),
@@ -1221,7 +1222,7 @@ mod backend_tests {
12211222
.map(|i| {
12221223
let content = format!("const IDX: usize = {i};").as_bytes().to_vec();
12231224
Blob {
1224-
mime: "text/rust".into(),
1225+
mime: mimes::TEXT_RUST.clone(),
12251226
content,
12261227
path: format!("{i}.rs"),
12271228
date_updated: now,
@@ -1286,7 +1287,7 @@ mod backend_tests {
12861287
path: (*path).to_string(),
12871288
content: b"foo\n".to_vec(),
12881289
compression: None,
1289-
mime: "text/plain".into(),
1290+
mime: mime::TEXT_PLAIN,
12901291
date_updated: Utc::now(),
12911292
})
12921293
.collect(),

src/storage/s3.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,12 @@ impl S3Backend {
214214

215215
Ok(Blob {
216216
path: path.into(),
217-
mime: res.content_type.unwrap(),
217+
mime: res
218+
.content_type
219+
.as_ref()
220+
.unwrap()
221+
.parse()
222+
.unwrap_or(mime::APPLICATION_OCTET_STREAM),
218223
date_updated,
219224
content: content.into_inner(),
220225
compression,
@@ -232,7 +237,7 @@ impl S3Backend {
232237
.bucket(&self.bucket)
233238
.key(&blob.path)
234239
.body(blob.content.clone().into())
235-
.content_type(&blob.mime)
240+
.content_type(blob.mime.to_string())
236241
.set_content_encoding(blob.compression.map(|alg| alg.to_string()))
237242
.send()
238243
.map_ok(|_| {

src/web/file.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use axum::{
1515
},
1616
response::{IntoResponse, Response as AxumResponse},
1717
};
18-
use mime::Mime;
1918

2019
#[derive(Debug)]
2120
pub(crate) struct File(pub(crate) Blob);
@@ -39,16 +38,10 @@ impl File {
3938

4039
impl IntoResponse for File {
4140
fn into_response(self) -> AxumResponse {
42-
let content_type: Mime = self
43-
.0
44-
.mime
45-
.parse::<Mime>()
46-
.unwrap_or(mime::APPLICATION_OCTET_STREAM);
47-
4841
(
4942
StatusCode::OK,
5043
[
51-
(CONTENT_TYPE, content_type.as_ref()),
44+
(CONTENT_TYPE, self.0.mime.as_ref()),
5245
(
5346
LAST_MODIFIED,
5447
&self.0.date_updated.format("%a, %d %b %Y %T %Z").to_string(),

src/web/source.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::{
1717
use anyhow::{Context as _, Result};
1818
use axum::{response::IntoResponse, Extension};
1919
use axum_extra::headers::HeaderMapExt;
20+
use mime::Mime;
2021
use rinja::Template;
2122
use semver::Version;
2223
use serde::Deserialize;
@@ -26,18 +27,16 @@ use tracing::instrument;
2627
/// A source file's name and mime type
2728
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
2829
struct File {
29-
/// The name of the file
3030
name: String,
31-
/// The mime type of the file
3231
mime: String,
3332
}
3433

3534
impl File {
36-
fn from_path_and_mime(path: &str, mime: &str) -> File {
35+
fn from_path_and_mime(path: &str, mime: &Mime) -> File {
3736
let (name, mime) = if let Some((dir, _)) = path.split_once('/') {
3837
(dir, "dir")
3938
} else {
40-
(path, mime)
39+
(path, mime.as_ref())
4140
};
4241

4342
Self {
@@ -106,7 +105,11 @@ impl FileList {
106105

107106
for file in files {
108107
if let Some(file) = file.as_array() {
109-
let mime = file[0].as_str().unwrap();
108+
let mime: Mime = file[0]
109+
.as_str()
110+
.unwrap()
111+
.parse()
112+
.unwrap_or(mime::APPLICATION_OCTET_STREAM);
110113
let path = file[1].as_str().unwrap();
111114

112115
// skip .cargo-ok generated by cargo
@@ -116,7 +119,7 @@ impl FileList {
116119

117120
// look only files for req_path
118121
if let Some(path) = path.strip_prefix(folder) {
119-
let file = File::from_path_and_mime(path, mime);
122+
let file = File::from_path_and_mime(path, &mime);
120123

121124
// avoid adding duplicates, a directory may occur more than once
122125
if !file_list.contains(&file) {
@@ -278,7 +281,7 @@ pub(crate) async fn source_browser_handler(
278281
));
279282

280283
let (file, file_content) = if let Some(blob) = blob {
281-
let is_text = blob.mime.starts_with("text") || blob.mime == "application/json";
284+
let is_text = blob.mime.type_() == mime::TEXT || blob.mime == mime::APPLICATION_JSON;
282285
// serve the file with DatabaseFileHandler if file isn't text and not empty
283286
if !is_text && !blob.is_empty() {
284287
let mut response = DbFile(blob).into_response();

0 commit comments

Comments
 (0)