forked from matteo/serves3
1
0
Fork 0

fix listing of S3 prefixes not terminated by a slash

This commit is contained in:
Eren Ay 2024-04-17 16:06:26 +02:00
parent 25576463cf
commit 0318729d3f
4 changed files with 89 additions and 3 deletions

57
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -78,6 +78,7 @@ enum FileView {
#[derive(Serialize)]
struct FileViewItem {
parent: String,
path: String,
size: String,
size_bytes: u64,
@ -159,7 +160,7 @@ async fn s3_fileview(path: &PathBuf) -> Result<Vec<FileViewItem>, 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<Vec<FileViewItem>, 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<Vec<FileViewItem>, 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);
}
}

View File

@ -44,7 +44,7 @@
{% for object in objects %}
<tr>
<td><a href="{{ object.path | urlencode }}" data-size-bytes="{{ object.size_bytes }}">{{ object.path }}</a></td>
<td><a href="/{{ object.parent | urlencode }}{{ object.path | urlencode }}" data-size-bytes="{{ object.size_bytes }}">{{ object.path }}</a></td>
<td>{{ object.size }}</td>
<td>{{ object.last_modification}}</td>
</tr>