bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
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,
    })
}