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")
}