use super::config::{
DEFAULT_NAMESPACE_MAX_BYTES, KEY_ROOT, MAX_SCAN_PAGE_LIMIT, META_SUFFIX, SWEEP_COUNT,
};
pub(crate) fn data_key(tenant: &str, namespace: &str, key: &str) -> String {
format!("{KEY_ROOT}:{tenant}:{namespace}:k:{key}")
}
pub(crate) fn data_match(tenant: &str, namespace: &str, key_prefix: &str) -> String {
format!("{KEY_ROOT}:{tenant}:{namespace}:k:{key_prefix}*")
}
pub(crate) fn namespace_match_all(tenant: &str, namespace: &str) -> String {
format!("{KEY_ROOT}:{tenant}:{namespace}:*")
}
pub(crate) fn bytes_counter_key(tenant: &str, namespace: &str) -> String {
format!("{KEY_ROOT}:{tenant}:{namespace}:__bytes__")
}
pub(crate) fn meta_key(tenant: &str, namespace: &str) -> String {
format!("{KEY_ROOT}:{tenant}:{namespace}:{META_SUFFIX}")
}
pub(crate) fn meta_match_all() -> String {
format!("{KEY_ROOT}:*:{META_SUFFIX}")
}
pub(crate) fn reconcile_cursor_key() -> String {
format!("{KEY_ROOT}:__reconcile__:cursor")
}
pub(crate) fn parse_meta_key(full_key: &str) -> Option<(String, String)> {
let rest = full_key.strip_prefix(&format!("{KEY_ROOT}:"))?;
let rest = rest.strip_suffix(&format!(":{META_SUFFIX}"))?;
let (tenant, namespace) = rest.rsplit_once(':')?;
(!tenant.trim().is_empty() && !namespace.trim().is_empty())
.then(|| (tenant.to_string(), namespace.to_string()))
}
pub(crate) fn reconciled_sum(lengths: &[i64]) -> i64 {
lengths
.iter()
.fold(0i64, |acc, len| acc.saturating_add((*len).max(0)))
}
pub(crate) fn clamped_scan_count(limit: i32) -> u32 {
if limit > 0 {
(limit as u32).min(MAX_SCAN_PAGE_LIMIT)
} else {
SWEEP_COUNT
}
}
pub(crate) fn strip_data_prefix<'a>(
full_key: &'a str,
tenant: &str,
namespace: &str,
) -> Option<&'a str> {
let prefix = format!("{KEY_ROOT}:{tenant}:{namespace}:k:");
full_key.strip_prefix(&prefix)
}
pub(crate) fn effective_max_bytes(declared: i64) -> i64 {
if declared > 0 {
declared
} else {
DEFAULT_NAMESPACE_MAX_BYTES
}
}
pub(crate) fn would_exceed_budget(used: i64, delta: i64, max_bytes: i64) -> bool {
delta > 0 && used.saturating_add(delta) > max_bytes
}