From 546485a8eea6aaad165f978159ff7bd489120d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=AD=A3=E8=B6=85?= Date: Sat, 14 Feb 2026 20:55:14 +0800 Subject: [PATCH] refactor(storage): extract list_parts parameter parsing (#1816) --- rustfs/src/storage/ecfs.rs | 13 ++------ rustfs/src/storage/s3_api/multipart.rs | 45 +++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index 4df6f552b..df7b0b754 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -25,7 +25,7 @@ use crate::storage::options::{filter_object_metadata, get_content_sha256}; use crate::storage::readers::InMemoryAsyncReader; use crate::storage::s3_api::bucket::{build_list_objects_output, build_list_objects_v2_output}; use crate::storage::s3_api::common::rustfs_owner; -use crate::storage::s3_api::multipart::{build_list_multipart_uploads_output, build_list_parts_output}; +use crate::storage::s3_api::multipart::{build_list_multipart_uploads_output, build_list_parts_output, parse_list_parts_params}; use crate::storage::sse::{ DecryptionRequest, EncryptionRequest, PrepareEncryptionRequest, check_encryption_metadata, sse_decryption, sse_encryption, sse_prepare_encryption, strip_managed_encryption_metadata, @@ -3801,16 +3801,7 @@ impl S3 for FS { return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string())); }; - let part_number_marker = part_number_marker.map(|x| x as usize); - let max_parts = match max_parts { - Some(parts) => { - if !(1..=1000).contains(&parts) { - return Err(s3_error!(InvalidArgument, "max-parts must be between 1 and 1000")); - } - parts as usize - } - None => 1000, - }; + let (part_number_marker, max_parts) = parse_list_parts_params(part_number_marker, max_parts)?; let res = store .list_object_parts(&bucket, &key, &upload_id, part_number_marker, max_parts, &ObjectOptions::default()) diff --git a/rustfs/src/storage/s3_api/multipart.rs b/rustfs/src/storage/s3_api/multipart.rs index 0eb4ac0b2..7fc5c22b3 100644 --- a/rustfs/src/storage/s3_api/multipart.rs +++ b/rustfs/src/storage/s3_api/multipart.rs @@ -16,6 +16,7 @@ use crate::storage::s3_api::common::{rustfs_initiator, rustfs_owner}; use rustfs_ecstore::client::object_api_utils::to_s3s_etag; use rustfs_ecstore::store_api::{ListMultipartsInfo, ListPartsInfo}; use s3s::dto::{CommonPrefix, ListMultipartUploadsOutput, ListPartsOutput, MultipartUpload, Part, Timestamp}; +use s3s::{S3Error, S3ErrorCode}; pub(crate) fn build_list_parts_output(res: ListPartsInfo) -> ListPartsOutput { let owner = rustfs_owner(); @@ -52,6 +53,27 @@ pub(crate) fn build_list_parts_output(res: ListPartsInfo) -> ListPartsOutput { } } +pub(crate) fn parse_list_parts_params( + part_number_marker: Option, + max_parts: Option, +) -> Result<(Option, usize), S3Error> { + let part_number_marker = part_number_marker.map(|x| x as usize); + let max_parts = match max_parts { + Some(parts) => { + if !(1..=1000).contains(&parts) { + return Err(S3Error::with_message( + S3ErrorCode::InvalidArgument, + "max-parts must be between 1 and 1000".to_string(), + )); + } + parts as usize + } + None => 1000, + }; + + Ok((part_number_marker, max_parts)) +} + pub(crate) fn build_list_multipart_uploads_output( bucket: String, prefix: String, @@ -90,9 +112,10 @@ pub(crate) fn build_list_multipart_uploads_output( #[cfg(test)] mod tests { - use super::{build_list_multipart_uploads_output, build_list_parts_output}; + use super::{build_list_multipart_uploads_output, build_list_parts_output, parse_list_parts_params}; use rustfs_ecstore::client::object_api_utils::to_s3s_etag; use rustfs_ecstore::store_api::{ListMultipartsInfo, ListPartsInfo, MultipartInfo, PartInfo}; + use s3s::S3ErrorCode; use s3s::dto::Timestamp; use time::OffsetDateTime; @@ -202,4 +225,24 @@ mod tests { assert_eq!(common_prefixes[0].prefix.as_deref(), Some("prefix-a/")); assert_eq!(common_prefixes[1].prefix.as_deref(), Some("prefix-b/")); } + + #[test] + fn test_parse_list_parts_params_defaults_and_valid_values() { + let (part_number_marker, max_parts) = parse_list_parts_params(Some(5), Some(100)).expect("expected valid params"); + assert_eq!(part_number_marker, Some(5)); + assert_eq!(max_parts, 100); + + let (part_number_marker, max_parts) = parse_list_parts_params(None, None).expect("expected default params"); + assert_eq!(part_number_marker, None); + assert_eq!(max_parts, 1000); + } + + #[test] + fn test_parse_list_parts_params_rejects_invalid_max_parts() { + let err = parse_list_parts_params(None, Some(0)).expect_err("expected invalid max_parts"); + assert_eq!(*err.code(), S3ErrorCode::InvalidArgument); + + let err = parse_list_parts_params(None, Some(1001)).expect_err("expected invalid max_parts"); + assert_eq!(*err.code(), S3ErrorCode::InvalidArgument); + } }