use super::*;
pub(crate) fn parse_bucket_object_lock_configuration(
bucket: &str,
body: &[u8],
) -> Result<BucketObjectLockConfiguration, RuntimeError> {
let xml = String::from_utf8_lossy(body);
let enabled = text_between(&xml, "<ObjectLockEnabled>", "</ObjectLockEnabled>")
.map(str::trim)
.map(|value| value == "Enabled")
.unwrap_or(true);
if !enabled {
return Err(RuntimeError::InvalidObjectLockConfiguration(
"ObjectLockEnabled must be Enabled".to_string(),
));
}
let default_retention =
if let Some(rule_xml) = text_between(&xml, "<DefaultRetention>", "</DefaultRetention>") {
let mode = text_between(rule_xml, "<Mode>", "</Mode>")
.map(str::trim)
.ok_or_else(|| {
RuntimeError::InvalidObjectLockConfiguration(
"DefaultRetention Mode is required".to_string(),
)
})?;
retention_mode_from_text(mode)?;
let days = text_between(rule_xml, "<Days>", "</Days>")
.map(str::trim)
.map(parse_positive_u64)
.transpose()?;
let years = text_between(rule_xml, "<Years>", "</Years>")
.map(str::trim)
.map(parse_positive_u64)
.transpose()?;
let default_retention = ObjectLockDefaultRetention {
mode: mode.to_ascii_uppercase(),
days,
years,
};
validate_default_retention(&Some(default_retention.clone()))?;
Some(default_retention)
} else {
None
};
Ok(BucketObjectLockConfiguration {
bucket: bucket.to_string(),
enabled,
default_retention,
})
}
pub(crate) fn parse_bucket_encryption_configuration(
body: &[u8],
) -> Result<ServerSideEncryption, RuntimeError> {
let xml = String::from_utf8_lossy(body);
let rule_xml = text_between(
&xml,
"<ApplyServerSideEncryptionByDefault>",
"</ApplyServerSideEncryptionByDefault>",
)
.ok_or_else(|| {
RuntimeError::InvalidEncryption(
"ApplyServerSideEncryptionByDefault is required".to_string(),
)
})?;
let algorithm = text_between(rule_xml, "<SSEAlgorithm>", "</SSEAlgorithm>")
.map(str::trim)
.ok_or_else(|| RuntimeError::InvalidEncryption("SSEAlgorithm is required".to_string()))?;
let kms_key_id = text_between(rule_xml, "<KMSMasterKeyID>", "</KMSMasterKeyID>")
.map(str::trim)
.filter(|value| !value.is_empty())
.map(str::to_string);
Ok(ServerSideEncryption {
algorithm: algorithm.to_string(),
kms_key_id,
})
}
pub(crate) fn parse_object_legal_hold(body: &[u8]) -> Result<bool, RuntimeError> {
let xml = String::from_utf8_lossy(body);
let status = text_between(&xml, "<Status>", "</Status>")
.map(str::trim)
.ok_or_else(|| RuntimeError::InvalidLegalHold("Status is required".to_string()))?;
parse_legal_hold_status(status)
}
pub(crate) fn parse_object_retention(body: &[u8]) -> Result<(String, u64), RuntimeError> {
let xml = String::from_utf8_lossy(body);
let mode = text_between(&xml, "<Mode>", "</Mode>")
.map(str::trim)
.ok_or_else(|| RuntimeError::InvalidRetention("Mode is required".to_string()))?;
retention_mode_from_text(mode)?;
let retain_until = text_between(&xml, "<RetainUntilDate>", "</RetainUntilDate>")
.map(str::trim)
.ok_or_else(|| RuntimeError::InvalidRetention("RetainUntilDate is required".to_string()))?;
Ok((
mode.to_ascii_uppercase(),
parse_retention_timestamp(retain_until)?,
))
}
pub(crate) fn validate_default_retention(
default_retention: &Option<ObjectLockDefaultRetention>,
) -> Result<(), RuntimeError> {
let Some(default_retention) = default_retention else {
return Ok(());
};
retention_mode_from_text(&default_retention.mode)?;
match (default_retention.days, default_retention.years) {
(Some(days), None) if days > 0 => Ok(()),
(None, Some(years)) if years > 0 => Ok(()),
_ => Err(RuntimeError::InvalidObjectLockConfiguration(
"DefaultRetention requires exactly one positive Days or Years value".to_string(),
)),
}
}