bucketwarden-server 0.1.0

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

pub(super) struct InventoryVersionView<'a> {
    pub(super) key: &'a str,
    pub(super) is_latest: bool,
    pub(super) version: &'a StoredVersion,
}

#[derive(Deserialize, Serialize)]
struct RuntimeInventoryContinuationToken {
    tenant_id: String,
    bucket: String,
    key: String,
    version_id: String,
}

pub(super) fn filtered_inventory_versions<'a>(
    bucket_state: &'a BucketState,
    key_prefix: &str,
    include_delete_markers: bool,
    include_noncurrent_versions: bool,
) -> Vec<InventoryVersionView<'a>> {
    let mut entries = Vec::new();
    for (key, object) in &bucket_state.objects {
        if !key.starts_with(key_prefix) {
            continue;
        }
        let latest_index = object.versions.len().saturating_sub(1);
        for (index, version) in object.versions.iter().enumerate() {
            let is_latest = index == latest_index;
            if !include_noncurrent_versions && !is_latest {
                continue;
            }
            if version.delete_marker && !include_delete_markers {
                continue;
            }
            entries.push(InventoryVersionView {
                key,
                is_latest,
                version,
            });
        }
    }
    entries
}

#[derive(Default)]
pub(super) struct InventorySummary {
    pub(super) current_object_count: usize,
    pub(super) noncurrent_version_count: usize,
    pub(super) delete_marker_count: usize,
    pub(super) encrypted_version_count: usize,
    pub(super) legal_hold_count: usize,
    pub(super) retained_version_count: usize,
    pub(super) replicated_version_count: usize,
    pub(super) total_bytes: usize,
}

impl InventorySummary {
    pub(super) fn observe(&mut self, view: InventoryVersionView<'_>) {
        if view.is_latest && !view.version.delete_marker {
            self.current_object_count += 1;
        }
        if !view.is_latest {
            self.noncurrent_version_count += 1;
        }
        if view.version.delete_marker {
            self.delete_marker_count += 1;
        }
        if view.version.metadata.encryption.is_some() {
            self.encrypted_version_count += 1;
        }
        if view.version.lock.legal_hold {
            self.legal_hold_count += 1;
        }
        if view.version.lock.retention_mode.is_some() {
            self.retained_version_count += 1;
        }
        if view.version.replication_status.is_some() {
            self.replicated_version_count += 1;
        }
        self.total_bytes += view.version.content_length();
    }
}

pub(super) fn runtime_inventory_continuation_start(
    entries: &[RuntimeInventoryEntry],
    token: &str,
) -> Result<usize, RuntimeError> {
    let token: RuntimeInventoryContinuationToken =
        serde_json::from_str(token).map_err(|_| RuntimeError::InvalidListParameter {
            name: "continuation-token".to_string(),
            value: token.to_string(),
        })?;
    let cursor = (
        token.tenant_id.as_str(),
        token.bucket.as_str(),
        token.key.as_str(),
        token.version_id.as_str(),
    );
    entries
        .iter()
        .position(|entry| {
            (
                entry.tenant_id.as_str(),
                entry.bucket.as_str(),
                entry.key.as_str(),
                entry.version_id.as_str(),
            ) == cursor
        })
        .map(|index| index + 1)
        .ok_or_else(|| RuntimeError::InvalidListParameter {
            name: "continuation-token".to_string(),
            value: token_to_string(&token),
        })
}

pub(super) fn runtime_inventory_page_window(
    entries: &[RuntimeInventoryEntry],
    max_entries: Option<usize>,
) -> (Vec<RuntimeInventoryEntry>, Option<String>) {
    let limit = max_entries.unwrap_or(entries.len());
    let page = entries.iter().take(limit).cloned().collect::<Vec<_>>();
    let next = if entries.len() > page.len() {
        page.last().map(runtime_inventory_continuation_token)
    } else {
        None
    };
    (page, next)
}

fn runtime_inventory_continuation_token(entry: &RuntimeInventoryEntry) -> String {
    token_to_string(&RuntimeInventoryContinuationToken {
        tenant_id: entry.tenant_id.clone(),
        bucket: entry.bucket.clone(),
        key: entry.key.clone(),
        version_id: entry.version_id.clone(),
    })
}

fn token_to_string(token: &RuntimeInventoryContinuationToken) -> String {
    serde_json::to_string(token).expect("runtime inventory continuation token is serializable")
}