use super::*;
pub(crate) fn parse_bucket_replication_configuration(
bucket: &str,
body: &[u8],
) -> Result<BucketReplicationConfiguration, RuntimeError> {
let xml = String::from_utf8_lossy(body);
let role = text_between(&xml, "<Role>", "</Role>")
.map(str::trim)
.filter(|value| !value.is_empty())
.map(str::to_string);
let mut rules = Vec::new();
let mut remainder = xml.as_ref();
while let Some(start) = remainder.find("<Rule>") {
remainder = &remainder[start + "<Rule>".len()..];
let Some(end) = remainder.find("</Rule>") else {
return Err(RuntimeError::InvalidReplicationConfiguration(
"unclosed Rule".to_string(),
));
};
let rule_xml = &remainder[..end];
remainder = &remainder[end + "</Rule>".len()..];
let id = text_between(rule_xml, "<ID>", "</ID>")
.or_else(|| text_between(rule_xml, "<Id>", "</Id>"))
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or("replication")
.to_string();
let status = text_between(rule_xml, "<Status>", "</Status>")
.map(str::trim)
.unwrap_or("Disabled")
.to_string();
let destination_xml = text_between(rule_xml, "<Destination>", "</Destination>")
.ok_or_else(|| {
RuntimeError::InvalidReplicationConfiguration(format!(
"rule {id} Destination is required"
))
})?;
let destination = Destination {
bucket: text_between(destination_xml, "<Bucket>", "</Bucket>")
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| {
RuntimeError::InvalidReplicationConfiguration(format!(
"rule {id} Destination Bucket is required"
))
})?
.to_string(),
};
let prefix = text_between(rule_xml, "<Prefix>", "</Prefix>")
.or_else(|| text_between(rule_xml, "<Filter><Prefix>", "</Prefix></Filter>"))
.map(str::trim)
.filter(|value| !value.is_empty())
.map(str::to_string);
let tag_filter = parse_rule_tag_filter(rule_xml)?;
let delete_marker_replication = text_between(
rule_xml,
"<DeleteMarkerReplication>",
"</DeleteMarkerReplication>",
)
.and_then(|xml| text_between(xml, "<Status>", "</Status>"))
.map(str::trim)
.is_some_and(|status| status == "Enabled");
let existing_object_replication = text_between(
rule_xml,
"<ExistingObjectReplication>",
"</ExistingObjectReplication>",
)
.and_then(|xml| text_between(xml, "<Status>", "</Status>"))
.map(str::trim)
.is_some_and(|status| status == "Enabled");
let replicate_encrypted_objects = text_between(
rule_xml,
"<SseKmsEncryptedObjects>",
"</SseKmsEncryptedObjects>",
)
.and_then(|xml| text_between(xml, "<Status>", "</Status>"))
.map(str::trim)
.is_some_and(|status| status == "Enabled");
rules.push(ReplicationRule {
id,
status,
destination_bucket: destination.bucket,
prefix,
tag_filter,
delete_marker_replication,
existing_object_replication,
replicate_encrypted_objects,
});
}
validate_replication_rules(&rules)?;
Ok(BucketReplicationConfiguration {
bucket: bucket.to_string(),
role,
rules,
})
}