Merge pull request 'Upgrade quick-xml crate to 0.39' (#1319) from gwenlg/garage:quick_xml_upgrade into main-v2

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1319
Reviewed-by: Alex <lx@deuxfleurs.fr>
This commit is contained in:
Alex
2026-02-20 21:29:26 +00:00
11 changed files with 139 additions and 98 deletions
Generated
+2 -2
View File
@@ -3737,9 +3737,9 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.26.0" version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" checksum = "f2e3bf4aa9d243beeb01a7b3bc30b77cfe2c44e24ec02d751a7104a53c2c49a1"
dependencies = [ dependencies = [
"memchr", "memchr",
"serde", "serde",
+1 -1
View File
@@ -95,7 +95,7 @@ fjall = "2.11"
async-compression = { version = "0.4", features = ["tokio", "zstd"] } async-compression = { version = "0.4", features = ["tokio", "zstd"] }
zstd = { version = "0.13", default-features = false } zstd = { version = "0.13", default-features = false }
quick-xml = { version = "0.26", features = ["serialize"] } quick-xml = { version = "0.39", features = ["serialize"] }
rmp-serde = "1.3" rmp-serde = "1.3"
serde = { version = "1.0", default-features = false, features = ["derive", "rc"] } serde = { version = "1.0", default-features = false, features = ["derive", "rc"] }
serde_bytes = "0.11" serde_bytes = "0.11"
+3 -2
View File
@@ -330,8 +330,8 @@ fn parse_create_bucket_xml(xml_bytes: &[u8]) -> Option<Option<String>> {
// Returns Some(None) if no location constraint is given // Returns Some(None) if no location constraint is given
// Returns Some(Some("xxxx")) where xxxx is the given location constraint // Returns Some(Some("xxxx")) where xxxx is the given location constraint
let xml_str = std::str::from_utf8(xml_bytes).ok()?; let xml_str = std::str::from_utf8(xml_bytes).ok()?.trim();
if xml_str.trim_matches(char::is_whitespace).is_empty() { if xml_str.is_empty() {
return Some(None); return Some(None);
} }
@@ -373,6 +373,7 @@ mod tests {
#[test] #[test]
fn create_bucket() { fn create_bucket() {
assert_eq!(parse_create_bucket_xml(br#""#), Some(None)); assert_eq!(parse_create_bucket_xml(br#""#), Some(None));
assert_eq!(parse_create_bucket_xml(br#" "#), Some(None));
assert_eq!( assert_eq!(
parse_create_bucket_xml( parse_create_bucket_xml(
br#" br#"
+1 -1
View File
@@ -853,7 +853,7 @@ pub struct CopyObjectResult {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct CopyPartResult { pub struct CopyPartResult {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "LastModified")] #[serde(rename = "LastModified")]
pub last_modified: s3_xml::Value, pub last_modified: s3_xml::Value,
+8 -6
View File
@@ -86,7 +86,7 @@ pub async fn handle_put_cors(
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename = "CORSConfiguration")] #[serde(rename = "CORSConfiguration")]
pub struct CorsConfiguration { pub struct CorsConfiguration {
#[serde(serialize_with = "xmlns_tag", skip_deserializing)] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag", skip_deserializing)]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "CORSRule")] #[serde(rename = "CORSRule")]
pub cors_rules: Vec<CorsRule>, pub cors_rules: Vec<CorsRule>,
@@ -94,9 +94,9 @@ pub struct CorsConfiguration {
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct CorsRule { pub struct CorsRule {
#[serde(rename = "ID")] #[serde(rename = "ID", skip_serializing_if = "Option::is_none")]
pub id: Option<Value>, pub id: Option<Value>,
#[serde(rename = "MaxAgeSeconds")] #[serde(rename = "MaxAgeSeconds", skip_serializing_if = "Option::is_none")]
pub max_age_seconds: Option<IntValue>, pub max_age_seconds: Option<IntValue>,
#[serde(rename = "AllowedOrigin")] #[serde(rename = "AllowedOrigin")]
pub allowed_origins: Vec<Value>, pub allowed_origins: Vec<Value>,
@@ -196,6 +196,8 @@ impl CorsRule {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::unprettify_xml;
use super::*; use super::*;
use quick_xml::de::from_str; use quick_xml::de::from_str;
@@ -228,7 +230,8 @@ mod tests {
<ExposeHeader>*</ExposeHeader> <ExposeHeader>*</ExposeHeader>
</CORSRule> </CORSRule>
</CORSConfiguration>"#; </CORSConfiguration>"#;
let conf: CorsConfiguration = from_str(message).unwrap(); let conf: CorsConfiguration =
from_str(message).expect("failed to deserialize xml into `CorsConfiguration` struct");
let ref_value = CorsConfiguration { let ref_value = CorsConfiguration {
xmlns: (), xmlns: (),
cors_rules: vec![ cors_rules: vec![
@@ -265,8 +268,7 @@ mod tests {
let message2 = to_xml_with_header(&ref_value)?; let message2 = to_xml_with_header(&ref_value)?;
let cleanup = |c: &str| c.replace(char::is_whitespace, ""); assert_eq!(unprettify_xml(message), unprettify_xml(&message2));
assert_eq!(cleanup(message), cleanup(&message2));
Ok(()) Ok(())
} }
+14 -14
View File
@@ -69,8 +69,16 @@ pub enum Error {
InvalidUtf8String(#[from] std::string::FromUtf8Error), InvalidUtf8String(#[from] std::string::FromUtf8Error),
/// The client sent invalid XML data /// The client sent invalid XML data
#[error("Invalid XML: {0}")] #[error("failed to deserialize XML")]
InvalidXml(String), InvalidXml(#[from] roxmltree::Error),
/// The client sent invalid XML data
#[error("XML deserialization failed")]
InvalidXmlDe(#[from] quick_xml::de::DeError),
/// The server failed to serialize data into XML
#[error("failed to serialize XML")]
InvalidXmlSe(#[from] quick_xml::se::SeError),
/// The client sent a range header with invalid value /// The client sent a range header with invalid value
#[error("Invalid HTTP range: {0:?}")] #[error("Invalid HTTP range: {0:?}")]
@@ -105,18 +113,6 @@ impl From<(http_range::HttpRangeParseError, u64)> for Error {
} }
} }
impl From<roxmltree::Error> for Error {
fn from(err: roxmltree::Error) -> Self {
Self::InvalidXml(format!("{}", err))
}
}
impl From<quick_xml::de::DeError> for Error {
fn from(err: quick_xml::de::DeError) -> Self {
Self::InvalidXml(format!("{}", err))
}
}
impl From<SignatureError> for Error { impl From<SignatureError> for Error {
fn from(err: SignatureError) -> Self { fn from(err: SignatureError) -> Self {
match err { match err {
@@ -149,6 +145,8 @@ impl Error {
Error::AuthorizationHeaderMalformed(_) => "AuthorizationHeaderMalformed", Error::AuthorizationHeaderMalformed(_) => "AuthorizationHeaderMalformed",
Error::NotImplemented(_) => "NotImplemented", Error::NotImplemented(_) => "NotImplemented",
Error::InvalidXml(_) => "MalformedXML", Error::InvalidXml(_) => "MalformedXML",
Error::InvalidXmlDe(_) => "MalformedXML",
Error::InvalidXmlSe(_) => "InternalError",
Error::InvalidRange(_) => "InvalidRange", Error::InvalidRange(_) => "InvalidRange",
Error::InvalidDigest(_) => "InvalidDigest", Error::InvalidDigest(_) => "InvalidDigest",
Error::InvalidUtf8Str(_) | Error::InvalidUtf8String(_) => "InvalidRequest", Error::InvalidUtf8Str(_) | Error::InvalidUtf8String(_) => "InvalidRequest",
@@ -166,6 +164,7 @@ impl ApiError for Error {
Error::PreconditionFailed => StatusCode::PRECONDITION_FAILED, Error::PreconditionFailed => StatusCode::PRECONDITION_FAILED,
Error::InvalidRange(_) => StatusCode::RANGE_NOT_SATISFIABLE, Error::InvalidRange(_) => StatusCode::RANGE_NOT_SATISFIABLE,
Error::NotImplemented(_) => StatusCode::NOT_IMPLEMENTED, Error::NotImplemented(_) => StatusCode::NOT_IMPLEMENTED,
Error::InvalidXmlSe(_) => StatusCode::INTERNAL_SERVER_ERROR,
Error::AuthorizationHeaderMalformed(_) Error::AuthorizationHeaderMalformed(_)
| Error::InvalidPart | Error::InvalidPart
| Error::InvalidPartOrder | Error::InvalidPartOrder
@@ -173,6 +172,7 @@ impl ApiError for Error {
| Error::InvalidDigest(_) | Error::InvalidDigest(_)
| Error::InvalidEncryptionAlgorithm(_) | Error::InvalidEncryptionAlgorithm(_)
| Error::InvalidXml(_) | Error::InvalidXml(_)
| Error::InvalidXmlDe(_)
| Error::InvalidUtf8Str(_) | Error::InvalidUtf8Str(_)
| Error::InvalidUtf8String(_) => StatusCode::BAD_REQUEST, | Error::InvalidUtf8String(_) => StatusCode::BAD_REQUEST,
} }
+8
View File
@@ -19,3 +19,11 @@ pub mod website;
mod encryption; mod encryption;
mod router; mod router;
pub mod xml; pub mod xml;
#[cfg(test)]
pub(crate) fn unprettify_xml(xml_in: &str) -> String {
xml_in.trim().lines().fold(String::new(), |mut val, line| {
val.push_str(line.trim());
val
})
}
+26 -14
View File
@@ -83,7 +83,7 @@ pub async fn handle_put_lifecycle(
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct LifecycleConfiguration { pub struct LifecycleConfiguration {
#[serde(serialize_with = "xmlns_tag", skip_deserializing)] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag", skip_deserializing)]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Rule")] #[serde(rename = "Rule")]
pub lifecycle_rules: Vec<LifecycleRule>, pub lifecycle_rules: Vec<LifecycleRule>,
@@ -91,35 +91,46 @@ pub struct LifecycleConfiguration {
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct LifecycleRule { pub struct LifecycleRule {
#[serde(rename = "ID")] #[serde(rename = "ID", skip_serializing_if = "Option::is_none")]
pub id: Option<Value>, pub id: Option<Value>,
#[serde(rename = "Status")] #[serde(rename = "Status")]
pub status: Value, pub status: Value,
#[serde(rename = "Filter", default)] #[serde(rename = "Filter", default, skip_serializing_if = "Option::is_none")]
pub filter: Option<Filter>, pub filter: Option<Filter>,
#[serde(rename = "Expiration", default)] #[serde(
rename = "Expiration",
default,
skip_serializing_if = "Option::is_none"
)]
pub expiration: Option<Expiration>, pub expiration: Option<Expiration>,
#[serde(rename = "AbortIncompleteMultipartUpload", default)] #[serde(
rename = "AbortIncompleteMultipartUpload",
default,
skip_serializing_if = "Option::is_none"
)]
pub abort_incomplete_mpu: Option<AbortIncompleteMpu>, pub abort_incomplete_mpu: Option<AbortIncompleteMpu>,
} }
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Default)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Filter { pub struct Filter {
#[serde(rename = "And")] #[serde(rename = "And", skip_serializing_if = "Option::is_none")]
pub and: Option<Box<Filter>>, pub and: Option<Box<Filter>>,
#[serde(rename = "Prefix")] #[serde(rename = "Prefix", skip_serializing_if = "Option::is_none")]
pub prefix: Option<Value>, pub prefix: Option<Value>,
#[serde(rename = "ObjectSizeGreaterThan")] #[serde(
rename = "ObjectSizeGreaterThan",
skip_serializing_if = "Option::is_none"
)]
pub size_gt: Option<IntValue>, pub size_gt: Option<IntValue>,
#[serde(rename = "ObjectSizeLessThan")] #[serde(rename = "ObjectSizeLessThan", skip_serializing_if = "Option::is_none")]
pub size_lt: Option<IntValue>, pub size_lt: Option<IntValue>,
} }
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct Expiration { pub struct Expiration {
#[serde(rename = "Days")] #[serde(rename = "Days", skip_serializing_if = "Option::is_none")]
pub days: Option<IntValue>, pub days: Option<IntValue>,
#[serde(rename = "Date")] #[serde(rename = "Date", skip_serializing_if = "Option::is_none")]
pub at_date: Option<Value>, pub at_date: Option<Value>,
} }
@@ -285,6 +296,8 @@ impl Expiration {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::unprettify_xml;
use super::*; use super::*;
use quick_xml::de::from_str; use quick_xml::de::from_str;
@@ -357,8 +370,7 @@ mod tests {
let message2 = to_xml_with_header(&ref_value)?; let message2 = to_xml_with_header(&ref_value)?;
let cleanup = |c: &str| c.replace(char::is_whitespace, ""); assert_eq!(unprettify_xml(message), unprettify_xml(&message2));
assert_eq!(cleanup(message), cleanup(&message2));
// Check validation // Check validation
let validated = ref_value let validated = ref_value
@@ -393,7 +405,7 @@ mod tests {
let message3 = to_xml_with_header(&LifecycleConfiguration::from_garage_lifecycle_config( let message3 = to_xml_with_header(&LifecycleConfiguration::from_garage_lifecycle_config(
&validated, &validated,
))?; ))?;
assert_eq!(cleanup(message), cleanup(&message3)); assert_eq!(unprettify_xml(message), unprettify_xml(&message3));
Ok(()) Ok(())
} }
+19 -11
View File
@@ -110,7 +110,7 @@ pub async fn handle_put_website(
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct WebsiteConfiguration { pub struct WebsiteConfiguration {
#[serde(serialize_with = "xmlns_tag", skip_deserializing)] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag", skip_deserializing)]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "ErrorDocument")] #[serde(rename = "ErrorDocument")]
pub error_document: Option<Key>, pub error_document: Option<Key>,
@@ -168,23 +168,29 @@ pub struct Target {
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct Condition { pub struct Condition {
#[serde(rename = "HttpErrorCodeReturnedEquals")] #[serde(
rename = "HttpErrorCodeReturnedEquals",
skip_serializing_if = "Option::is_none"
)]
pub http_error_code: Option<IntValue>, pub http_error_code: Option<IntValue>,
#[serde(rename = "KeyPrefixEquals")] #[serde(rename = "KeyPrefixEquals", skip_serializing_if = "Option::is_none")]
pub prefix: Option<Value>, pub prefix: Option<Value>,
} }
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct Redirect { pub struct Redirect {
#[serde(rename = "HostName")] #[serde(rename = "HostName", skip_serializing_if = "Option::is_none")]
pub hostname: Option<Value>, pub hostname: Option<Value>,
#[serde(rename = "Protocol")] #[serde(rename = "Protocol", skip_serializing_if = "Option::is_none")]
pub protocol: Option<Value>, pub protocol: Option<Value>,
#[serde(rename = "HttpRedirectCode")] #[serde(rename = "HttpRedirectCode", skip_serializing_if = "Option::is_none")]
pub http_redirect_code: Option<IntValue>, pub http_redirect_code: Option<IntValue>,
#[serde(rename = "ReplaceKeyPrefixWith")] #[serde(
rename = "ReplaceKeyPrefixWith",
skip_serializing_if = "Option::is_none"
)]
pub replace_prefix: Option<Value>, pub replace_prefix: Option<Value>,
#[serde(rename = "ReplaceKeyWith")] #[serde(rename = "ReplaceKeyWith", skip_serializing_if = "Option::is_none")]
pub replace_full: Option<Value>, pub replace_full: Option<Value>,
} }
@@ -373,6 +379,8 @@ impl Redirect {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::unprettify_xml;
use super::*; use super::*;
use quick_xml::de::from_str; use quick_xml::de::from_str;
@@ -416,7 +424,8 @@ mod tests {
</RoutingRule> </RoutingRule>
</RoutingRules> </RoutingRules>
</WebsiteConfiguration>"#; </WebsiteConfiguration>"#;
let conf: WebsiteConfiguration = from_str(message).unwrap(); let conf: WebsiteConfiguration =
from_str(message).expect("failed to deserialize xml in `WebsiteConfiguration`");
let ref_value = WebsiteConfiguration { let ref_value = WebsiteConfiguration {
xmlns: (), xmlns: (),
error_document: Some(Key { error_document: Some(Key {
@@ -467,8 +476,7 @@ mod tests {
let message2 = to_xml_with_header(&ref_value)?; let message2 = to_xml_with_header(&ref_value)?;
let cleanup = |c: &str| c.replace(char::is_whitespace, ""); assert_eq!(unprettify_xml(message), unprettify_xml(&message2));
assert_eq!(cleanup(message), cleanup(&message2));
Ok(()) Ok(())
} }
+56 -46
View File
@@ -1,11 +1,15 @@
use quick_xml::se::to_string; use quick_xml::se::{self, EmptyElementHandling, QuoteLevel};
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use crate::error::Error as ApiError; use crate::error::Error as ApiError;
pub fn to_xml_with_header<T: Serialize>(x: &T) -> Result<String, ApiError> { pub fn to_xml_with_header<T: Serialize>(x: &T) -> Result<String, ApiError> {
let mut xml = r#"<?xml version="1.0" encoding="UTF-8"?>"#.to_string(); let mut xml = r#"<?xml version="1.0" encoding="UTF-8"?>"#.to_string();
xml.push_str(&to_string(x)?);
let mut ser = se::Serializer::new(&mut xml);
ser.set_quote_level(QuoteLevel::Full)
.empty_element_handling(EmptyElementHandling::Expanded);
let _serialized = x.serialize(ser)?;
Ok(xml) Ok(xml)
} }
@@ -61,7 +65,7 @@ pub struct ListAllMyBucketsResult {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct LocationConstraint { pub struct LocationConstraint {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "$value")] #[serde(rename = "$value")]
pub region: String, pub region: String,
@@ -97,13 +101,13 @@ pub struct DeleteError {
pub key: Option<Value>, pub key: Option<Value>,
#[serde(rename = "Message")] #[serde(rename = "Message")]
pub message: Value, pub message: Value,
#[serde(rename = "VersionId")] #[serde(rename = "VersionId", skip_serializing_if = "Option::is_none")]
pub version_id: Option<Value>, pub version_id: Option<Value>,
} }
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct DeleteResult { pub struct DeleteResult {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Deleted")] #[serde(rename = "Deleted")]
pub deleted: Vec<Deleted>, pub deleted: Vec<Deleted>,
@@ -113,7 +117,7 @@ pub struct DeleteResult {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct InitiateMultipartUploadResult { pub struct InitiateMultipartUploadResult {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Bucket")] #[serde(rename = "Bucket")]
pub bucket: Value, pub bucket: Value,
@@ -125,7 +129,7 @@ pub struct InitiateMultipartUploadResult {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct CompleteMultipartUploadResult { pub struct CompleteMultipartUploadResult {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Location")] #[serde(rename = "Location")]
pub location: Option<Value>, pub location: Option<Value>,
@@ -135,17 +139,17 @@ pub struct CompleteMultipartUploadResult {
pub key: Value, pub key: Value,
#[serde(rename = "ETag")] #[serde(rename = "ETag")]
pub etag: Value, pub etag: Value,
#[serde(rename = "ChecksumCRC32")] #[serde(rename = "ChecksumCRC32", skip_serializing_if = "Option::is_none")]
pub checksum_crc32: Option<Value>, pub checksum_crc32: Option<Value>,
#[serde(rename = "ChecksumCRC32C")] #[serde(rename = "ChecksumCRC32C", skip_serializing_if = "Option::is_none")]
pub checksum_crc32c: Option<Value>, pub checksum_crc32c: Option<Value>,
#[serde(rename = "ChecksumCR64NVME")] #[serde(rename = "ChecksumCR64NVME", skip_serializing_if = "Option::is_none")]
pub checksum_crc64nvme: Option<Value>, pub checksum_crc64nvme: Option<Value>,
#[serde(rename = "ChecksumSHA1")] #[serde(rename = "ChecksumSHA1", skip_serializing_if = "Option::is_none")]
pub checksum_sha1: Option<Value>, pub checksum_sha1: Option<Value>,
#[serde(rename = "ChecksumSHA256")] #[serde(rename = "ChecksumSHA256", skip_serializing_if = "Option::is_none")]
pub checksum_sha256: Option<Value>, pub checksum_sha256: Option<Value>,
#[serde(rename = "ChecksumType")] #[serde(rename = "ChecksumType", skip_serializing_if = "Option::is_none")]
pub checksum_type: Option<Value>, pub checksum_type: Option<Value>,
} }
@@ -175,21 +179,21 @@ pub struct ListMultipartItem {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct ListMultipartUploadsResult { pub struct ListMultipartUploadsResult {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Bucket")] #[serde(rename = "Bucket")]
pub bucket: Value, pub bucket: Value,
#[serde(rename = "KeyMarker")] #[serde(rename = "KeyMarker", skip_serializing_if = "Option::is_none")]
pub key_marker: Option<Value>, pub key_marker: Option<Value>,
#[serde(rename = "UploadIdMarker")] #[serde(rename = "UploadIdMarker", skip_serializing_if = "Option::is_none")]
pub upload_id_marker: Option<Value>, pub upload_id_marker: Option<Value>,
#[serde(rename = "NextKeyMarker")] #[serde(rename = "NextKeyMarker", skip_serializing_if = "Option::is_none")]
pub next_key_marker: Option<Value>, pub next_key_marker: Option<Value>,
#[serde(rename = "NextUploadIdMarker")] #[serde(rename = "NextUploadIdMarker", skip_serializing_if = "Option::is_none")]
pub next_upload_id_marker: Option<Value>, pub next_upload_id_marker: Option<Value>,
#[serde(rename = "Prefix")] #[serde(rename = "Prefix")]
pub prefix: Value, pub prefix: Value,
#[serde(rename = "Delimiter")] #[serde(rename = "Delimiter", skip_serializing_if = "Option::is_none")]
pub delimiter: Option<Value>, pub delimiter: Option<Value>,
#[serde(rename = "MaxUploads")] #[serde(rename = "MaxUploads")]
pub max_uploads: IntValue, pub max_uploads: IntValue,
@@ -199,7 +203,7 @@ pub struct ListMultipartUploadsResult {
pub upload: Vec<ListMultipartItem>, pub upload: Vec<ListMultipartItem>,
#[serde(rename = "CommonPrefixes")] #[serde(rename = "CommonPrefixes")]
pub common_prefixes: Vec<CommonPrefix>, pub common_prefixes: Vec<CommonPrefix>,
#[serde(rename = "EncodingType")] #[serde(rename = "EncodingType", skip_serializing_if = "Option::is_none")]
pub encoding_type: Option<Value>, pub encoding_type: Option<Value>,
} }
@@ -213,21 +217,21 @@ pub struct PartItem {
pub part_number: IntValue, pub part_number: IntValue,
#[serde(rename = "Size")] #[serde(rename = "Size")]
pub size: IntValue, pub size: IntValue,
#[serde(rename = "ChecksumCRC32")] #[serde(rename = "ChecksumCRC32", skip_serializing_if = "Option::is_none")]
pub checksum_crc32: Option<Value>, pub checksum_crc32: Option<Value>,
#[serde(rename = "ChecksumCRC32C")] #[serde(rename = "ChecksumCRC32C", skip_serializing_if = "Option::is_none")]
pub checksum_crc32c: Option<Value>, pub checksum_crc32c: Option<Value>,
#[serde(rename = "ChecksumCRC64NVME")] #[serde(rename = "ChecksumCRC64NVME", skip_serializing_if = "Option::is_none")]
pub checksum_crc64nvme: Option<Value>, pub checksum_crc64nvme: Option<Value>,
#[serde(rename = "ChecksumSHA1")] #[serde(rename = "ChecksumSHA1", skip_serializing_if = "Option::is_none")]
pub checksum_sha1: Option<Value>, pub checksum_sha1: Option<Value>,
#[serde(rename = "ChecksumSHA256")] #[serde(rename = "ChecksumSHA256", skip_serializing_if = "Option::is_none")]
pub checksum_sha256: Option<Value>, pub checksum_sha256: Option<Value>,
} }
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct ListPartsResult { pub struct ListPartsResult {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Bucket")] #[serde(rename = "Bucket")]
pub bucket: Value, pub bucket: Value,
@@ -235,9 +239,12 @@ pub struct ListPartsResult {
pub key: Value, pub key: Value,
#[serde(rename = "UploadId")] #[serde(rename = "UploadId")]
pub upload_id: Value, pub upload_id: Value,
#[serde(rename = "PartNumberMarker")] #[serde(rename = "PartNumberMarker", skip_serializing_if = "Option::is_none")]
pub part_number_marker: Option<IntValue>, pub part_number_marker: Option<IntValue>,
#[serde(rename = "NextPartNumberMarker")] #[serde(
rename = "NextPartNumberMarker",
skip_serializing_if = "Option::is_none"
)]
pub next_part_number_marker: Option<IntValue>, pub next_part_number_marker: Option<IntValue>,
#[serde(rename = "MaxParts")] #[serde(rename = "MaxParts")]
pub max_parts: IntValue, pub max_parts: IntValue,
@@ -275,29 +282,32 @@ pub struct CommonPrefix {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct ListBucketResult { pub struct ListBucketResult {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Name")] #[serde(rename = "Name")]
pub name: Value, pub name: Value,
#[serde(rename = "Prefix")] #[serde(rename = "Prefix")]
pub prefix: Value, pub prefix: Value,
#[serde(rename = "Marker")] #[serde(rename = "Marker", skip_serializing_if = "Option::is_none")]
pub marker: Option<Value>, pub marker: Option<Value>,
#[serde(rename = "NextMarker")] #[serde(rename = "NextMarker", skip_serializing_if = "Option::is_none")]
pub next_marker: Option<Value>, pub next_marker: Option<Value>,
#[serde(rename = "StartAfter")] #[serde(rename = "StartAfter", skip_serializing_if = "Option::is_none")]
pub start_after: Option<Value>, pub start_after: Option<Value>,
#[serde(rename = "ContinuationToken")] #[serde(rename = "ContinuationToken", skip_serializing_if = "Option::is_none")]
pub continuation_token: Option<Value>, pub continuation_token: Option<Value>,
#[serde(rename = "NextContinuationToken")] #[serde(
rename = "NextContinuationToken",
skip_serializing_if = "Option::is_none"
)]
pub next_continuation_token: Option<Value>, pub next_continuation_token: Option<Value>,
#[serde(rename = "KeyCount")] #[serde(rename = "KeyCount", skip_serializing_if = "Option::is_none")]
pub key_count: Option<IntValue>, pub key_count: Option<IntValue>,
#[serde(rename = "MaxKeys")] #[serde(rename = "MaxKeys")]
pub max_keys: IntValue, pub max_keys: IntValue,
#[serde(rename = "Delimiter")] #[serde(rename = "Delimiter", skip_serializing_if = "Option::is_none")]
pub delimiter: Option<Value>, pub delimiter: Option<Value>,
#[serde(rename = "EncodingType")] #[serde(rename = "EncodingType", skip_serializing_if = "Option::is_none")]
pub encoding_type: Option<Value>, pub encoding_type: Option<Value>,
#[serde(rename = "IsTruncated")] #[serde(rename = "IsTruncated")]
pub is_truncated: Value, pub is_truncated: Value,
@@ -309,15 +319,15 @@ pub struct ListBucketResult {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct VersioningConfiguration { pub struct VersioningConfiguration {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Status")] #[serde(rename = "Status", skip_serializing_if = "Option::is_none")]
pub status: Option<Value>, pub status: Option<Value>,
} }
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct PostObject { pub struct PostObject {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Location")] #[serde(rename = "Location")]
pub location: Value, pub location: Value,
@@ -331,11 +341,11 @@ pub struct PostObject {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct Grantee { pub struct Grantee {
#[serde(rename = "xmlns:xsi", serialize_with = "xmlns_xsi_tag")] #[serde(rename = "@xmlns:xsi", serialize_with = "xmlns_xsi_tag")]
pub xmlns_xsi: (), pub xmlns_xsi: (),
#[serde(rename = "xsi:type")] #[serde(rename = "@xsi:type")]
pub typ: String, pub typ: String,
#[serde(rename = "DisplayName")] #[serde(rename = "DisplayName", skip_serializing_if = "Option::is_none")]
pub display_name: Option<Value>, pub display_name: Option<Value>,
#[serde(rename = "ID")] #[serde(rename = "ID")]
pub id: Option<Value>, pub id: Option<Value>,
@@ -357,9 +367,9 @@ pub struct AccessControlList {
#[derive(Debug, Serialize, PartialEq, Eq)] #[derive(Debug, Serialize, PartialEq, Eq)]
pub struct AccessControlPolicy { pub struct AccessControlPolicy {
#[serde(serialize_with = "xmlns_tag")] #[serde(rename = "@xmlns", serialize_with = "xmlns_tag")]
pub xmlns: (), pub xmlns: (),
#[serde(rename = "Owner")] #[serde(rename = "Owner", skip_serializing_if = "Option::is_none")]
pub owner: Option<Owner>, pub owner: Option<Owner>,
#[serde(rename = "AccessControlList")] #[serde(rename = "AccessControlList")]
pub acl: AccessControlList, pub acl: AccessControlList,
@@ -458,7 +468,7 @@ mod tests {
assert_eq!( assert_eq!(
to_xml_with_header(&get_bucket_versioning)?, to_xml_with_header(&get_bucket_versioning)?,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
<VersioningConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"/>" <VersioningConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"></VersioningConfiguration>"
); );
let get_bucket_versioning2 = VersioningConfiguration { let get_bucket_versioning2 = VersioningConfiguration {
xmlns: (), xmlns: (),
+1 -1
View File
@@ -466,7 +466,7 @@ async fn test_uploadlistpart() {
.await .await
.unwrap(); .unwrap();
assert!(r.part_number_marker.is_none()); assert_eq!(r.part_number_marker.as_deref(), None);
assert_eq!(r.next_part_number_marker.as_deref(), Some("1")); assert_eq!(r.next_part_number_marker.as_deref(), Some("1"));
assert_eq!(r.max_parts.unwrap(), 1_i32); assert_eq!(r.max_parts.unwrap(), 1_i32);
assert!(r.is_truncated.unwrap()); assert!(r.is_truncated.unwrap());