From 0318729d3f7d2312626c20b0262e015dba47ea2d Mon Sep 17 00:00:00 2001 From: Eren Ay Date: Wed, 17 Apr 2024 16:06:26 +0200 Subject: [PATCH] fix listing of S3 prefixes not terminated by a slash --- Cargo.lock | 57 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +++ src/main.rs | 30 +++++++++++++++++++-- templates/index.html.tera | 2 +- 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a5d4fb..231af2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -620,6 +620,12 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.29" @@ -1660,6 +1666,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "relative-path" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" + [[package]] name = "reqwest" version = "0.11.22" @@ -1805,6 +1817,35 @@ dependencies = [ "serde", ] +[[package]] +name = "rstest" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5316d2a1479eeef1ea21e7f9ddc67c191d497abc8fc3ba2467857abbb68330" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04a9df72cc1f67020b0d63ad9bfe4a323e459ea7eb68e03bd9824db49f9a4c25" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.39", + "unicode-ident", +] + [[package]] name = "rust-ini" version = "0.18.0" @@ -1853,6 +1894,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.25" @@ -1931,6 +1981,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + [[package]] name = "serde" version = "1.0.193" @@ -1992,6 +2048,7 @@ dependencies = [ "lazy_static", "rocket", "rocket_dyn_templates", + "rstest", "rust-s3", "serde", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index fade79b..380c962 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,6 @@ rocket_dyn_templates = { version = "0.1.0", features = ["tera"] } rust-s3 = { version = "0.33", default-features = false, features = ["tokio-native-tls"] } serde = { version = "1.0" } tempfile = { version = "3.6" } + +[dev-dependencies] +rstest = "0.19" diff --git a/src/main.rs b/src/main.rs index b8ecbdf..d220ba6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,6 +78,7 @@ enum FileView { #[derive(Serialize)] struct FileViewItem { + parent: String, path: String, size: String, size_bytes: u64, @@ -106,7 +107,7 @@ async fn index(path: PathBuf) -> Result { We try first to retrieve list an object as a file. If we fail, we fallback to retrieving the equivalent folder. */ - + if let Ok(result) = s3_serve_file(&path).await { Ok(result) } else { @@ -159,7 +160,7 @@ async fn s3_fileview(path: &PathBuf) -> Result, Error> { }; let s3_objects = BUCKET - .list(s3_folder_path, Some("/".into())) + .list(s3_folder_path.clone(), Some("/".into())) .await .map_err(|_| Error::NotFound("Object not found".into()))?; @@ -175,6 +176,7 @@ async fn s3_fileview(path: &PathBuf) -> Result, Error> { let folders = list.common_prefixes.iter().flatten().map(|dir| { let path = dir.prefix.strip_prefix(&prefix); path.map(|path| FileViewItem { + parent:s3_folder_path.clone(), path: path.to_owned(), size_bytes: 0, size: "[DIR]".to_owned(), @@ -185,6 +187,7 @@ async fn s3_fileview(path: &PathBuf) -> Result, Error> { let files = list.contents.iter().map(|obj| { let path = obj.key.strip_prefix(&prefix); path.map(|path| FileViewItem { + parent:s3_folder_path.clone(), path: path.to_owned(), size_bytes: obj.size, size: size_bytes_to_human(obj.size), @@ -238,3 +241,26 @@ fn rocket() -> _ { .unwrap() })) } + +// Test section starts + +#[cfg(test)] +mod tests { + use rstest::rstest; + // Note this useful idiom: importing names from outer (for mod tests) scope. + use super::*; + + #[rstest] + #[case(1024, "1.024 kB")] + #[case(10240, "10.240 kB")] + #[case(1024*1024, "1.049 MB")] + #[case(1024*1024*1024, "1.074 GB")] + #[case(0, "0.000 B")] + #[case(u64::MAX, format!("{:.3} GB",u64::MAX as f64/(1_000_000_000.0)))] + #[case(u64::MIN, format!("{:.3} B",u64::MIN as f64))] + + fn test_size_bytes_to_human(#[case] bytes: u64, #[case] expected: String) { + println!("{}",size_bytes_to_human(bytes)); + assert_eq!(size_bytes_to_human(bytes), expected); + } +} diff --git a/templates/index.html.tera b/templates/index.html.tera index 9401663..bd7a080 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -44,7 +44,7 @@ {% for object in objects %} - {{ object.path }} + {{ object.path }} {{ object.size }} {{ object.last_modification}}