bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
use super::*;

pub(crate) fn list_objects_xml(result: &ListObjectsResult, list_type_v2: bool) -> String {
    let contents = result
        .objects
        .iter()
        .map(|object| {
            let owner = object
                .owner
                .as_ref()
                .map(|owner| {
                    format!(
                        "<Owner><ID>{}</ID><DisplayName>{}</DisplayName></Owner>",
                        xml_escape(owner),
                        xml_escape(owner)
                    )
                })
                .unwrap_or_default();
            format!(
                "<Contents><Key>{}</Key><ETag>{}</ETag><LastModified>{}</LastModified><VersionId>{}</VersionId><Size>{}</Size>{owner}</Contents>",
                list_xml_value(&object.key, result),
                xml_escape(&quote_etag(&object.etag)),
                object.last_modified_epoch_seconds,
                xml_escape(&object.version_id),
                object.content_length
            )
        })
        .collect::<String>();
    let common_prefixes = result
        .common_prefixes
        .iter()
        .map(|prefix| {
            format!(
                "<CommonPrefixes><Prefix>{}</Prefix></CommonPrefixes>",
                list_xml_value(&prefix.prefix, result)
            )
        })
        .collect::<String>();
    let prefix = result
        .prefix
        .as_ref()
        .map(|prefix| format!("<Prefix>{}</Prefix>", list_xml_value(prefix, result)))
        .unwrap_or_default();
    let delimiter = result
        .delimiter
        .as_ref()
        .map(|delimiter| {
            format!(
                "<Delimiter>{}</Delimiter>",
                list_xml_value(delimiter, result)
            )
        })
        .unwrap_or_default();
    let encoding_type = result
        .encoding_type
        .as_ref()
        .map(|encoding_type| format!("<EncodingType>{}</EncodingType>", xml_escape(encoding_type)))
        .unwrap_or_default();
    let marker = result
        .marker
        .as_ref()
        .map(|marker| format!("<Marker>{}</Marker>", list_xml_value(marker, result)))
        .unwrap_or_default();
    let next_marker = result
        .next_marker
        .as_ref()
        .map(|marker| format!("<NextMarker>{}</NextMarker>", list_xml_value(marker, result)))
        .unwrap_or_default();
    let next_token = result
        .next_continuation_token
        .as_ref()
        .map(|token| {
            format!(
                "<NextContinuationToken>{}</NextContinuationToken>",
                list_xml_value(token, result)
            )
        })
        .unwrap_or_default();
    let continuation_token = result
        .continuation_token
        .as_ref()
        .map(|token| {
            format!(
                "<ContinuationToken>{}</ContinuationToken>",
                list_xml_value(token, result)
            )
        })
        .unwrap_or_default();
    let start_after = result
        .start_after
        .as_ref()
        .map(|start_after| {
            format!(
                "<StartAfter>{}</StartAfter>",
                list_xml_value(start_after, result)
            )
        })
        .unwrap_or_default();
    if list_type_v2 {
        return format!(
            "<ListBucketResult><Name>{}</Name>{prefix}{delimiter}{encoding_type}<KeyCount>{}</KeyCount><MaxKeys>{}</MaxKeys><IsTruncated>{}</IsTruncated>{continuation_token}{start_after}{next_token}{contents}{common_prefixes}</ListBucketResult>",
            xml_escape(&result.bucket),
            result.key_count,
            result.max_keys,
            result.is_truncated
        );
    }
    format!(
        "<ListBucketResult><Name>{}</Name>{prefix}{marker}{delimiter}{encoding_type}<MaxKeys>{}</MaxKeys><IsTruncated>{}</IsTruncated>{next_marker}{contents}{common_prefixes}</ListBucketResult>",
        xml_escape(&result.bucket),
        result.max_keys,
        result.is_truncated
    )
}

pub(crate) fn list_xml_value(value: &str, result: &ListObjectsResult) -> String {
    if result.encoding_type.as_deref() == Some("url") {
        percent_encode(value)
    } else {
        xml_escape(value)
    }
}

pub(crate) fn list_object_versions_xml(result: &ListObjectVersionsResult) -> String {
    let versions = result
        .versions
        .iter()
        .map(|version| {
            let owner = version
                .owner
                .as_ref()
                .map(|owner| {
                    format!(
                        "<Owner><ID>{}</ID><DisplayName>{}</DisplayName></Owner>",
                        xml_escape(owner),
                        xml_escape(owner)
                    )
                })
                .unwrap_or_default();
            format!(
                "<Version><Key>{}</Key><VersionId>{}</VersionId><IsLatest>{}</IsLatest><LastModified>{}</LastModified><ETag>{}</ETag><Size>{}</Size>{owner}</Version>",
                version_list_xml_value(&version.key, result),
                xml_escape(&version.version_id),
                version.is_latest,
                version.last_modified_epoch_seconds,
                xml_escape(&quote_etag(&version.etag)),
                version.content_length
            )
        })
        .collect::<String>();
    let delete_markers = result
        .delete_markers
        .iter()
        .map(|marker| {
            format!(
                "<DeleteMarker><Key>{}</Key><VersionId>{}</VersionId><IsLatest>{}</IsLatest><LastModified>{}</LastModified></DeleteMarker>",
                version_list_xml_value(&marker.key, result),
                xml_escape(&marker.version_id),
                marker.is_latest,
                marker.last_modified_epoch_seconds
            )
        })
        .collect::<String>();
    let prefix = result
        .prefix
        .as_ref()
        .map(|prefix| {
            format!(
                "<Prefix>{}</Prefix>",
                version_list_xml_value(prefix, result)
            )
        })
        .unwrap_or_default();
    let key_marker = result
        .key_marker
        .as_ref()
        .map(|marker| {
            format!(
                "<KeyMarker>{}</KeyMarker>",
                version_list_xml_value(marker, result)
            )
        })
        .unwrap_or_default();
    let version_id_marker = result
        .version_id_marker
        .as_ref()
        .map(|marker| format!("<VersionIdMarker>{}</VersionIdMarker>", xml_escape(marker)))
        .unwrap_or_default();
    let next_key_marker = result
        .next_key_marker
        .as_ref()
        .map(|marker| {
            format!(
                "<NextKeyMarker>{}</NextKeyMarker>",
                version_list_xml_value(marker, result)
            )
        })
        .unwrap_or_default();
    let next_version_id_marker = result
        .next_version_id_marker
        .as_ref()
        .map(|marker| {
            format!(
                "<NextVersionIdMarker>{}</NextVersionIdMarker>",
                xml_escape(marker)
            )
        })
        .unwrap_or_default();
    let encoding_type = result
        .encoding_type
        .as_ref()
        .map(|encoding_type| format!("<EncodingType>{}</EncodingType>", xml_escape(encoding_type)))
        .unwrap_or_default();
    format!(
        "<ListVersionsResult><Name>{}</Name>{prefix}{encoding_type}{key_marker}{version_id_marker}<MaxKeys>{}</MaxKeys><IsTruncated>{}</IsTruncated><KeyCount>{}</KeyCount>{next_key_marker}{next_version_id_marker}{versions}{delete_markers}</ListVersionsResult>",
        xml_escape(&result.bucket),
        result.max_keys,
        result.is_truncated,
        result.key_count
    )
}

pub(crate) fn object_tagging_xml(result: &ObjectTaggingResult) -> String {
    tags_xml(&result.tags)
}

pub(crate) fn object_legal_hold_xml(result: &ObjectLegalHoldResult) -> String {
    let status = if result.enabled { "ON" } else { "OFF" };
    format!("<LegalHold><Status>{status}</Status></LegalHold>")
}

pub(crate) fn object_retention_xml(result: &ObjectRetentionResult) -> String {
    match (&result.mode, result.retain_until_epoch_seconds) {
        (Some(mode), Some(retain_until)) => format!(
            "<Retention><Mode>{}</Mode><RetainUntilDate>{}</RetainUntilDate></Retention>",
            xml_escape(mode),
            format_retention_timestamp(retain_until)
        ),
        _ => "<Retention></Retention>".to_string(),
    }
}

pub(crate) fn tags_xml(tags: &BTreeMap<String, String>) -> String {
    let tags = tags
        .iter()
        .map(|(key, value)| {
            format!(
                "<Tag><Key>{}</Key><Value>{}</Value></Tag>",
                xml_escape(key),
                xml_escape(value)
            )
        })
        .collect::<String>();
    format!("<Tagging><TagSet>{tags}</TagSet></Tagging>")
}

pub(crate) fn version_list_xml_value(value: &str, result: &ListObjectVersionsResult) -> String {
    if result.encoding_type.as_deref() == Some("url") {
        percent_encode(value)
    } else {
        xml_escape(value)
    }
}

pub(crate) fn copy_object_xml(result: &CopyObjectResult) -> String {
    format!(
        "<CopyObjectResult><ETag>{}</ETag><VersionId>{}</VersionId></CopyObjectResult>",
        xml_escape(&quote_etag(&result.etag)),
        xml_escape(&result.version_id)
    )
}