pub const DEFAULT_KEY_PREFIX: &str = "yugendb";
pub const KEY_SEPARATOR: &str = ":";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RedisKeyspace {
pub key_prefix: String,
}
impl RedisKeyspace {
#[must_use]
pub fn new(key_prefix: impl Into<String>) -> Self {
Self {
key_prefix: key_prefix.into(),
}
}
}
fn encode_component(value: &str) -> String {
let mut encoded = String::new();
for byte in value.as_bytes() {
match *byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'_' | b'-' | b'.' => {
encoded.push(*byte as char)
}
other => encoded.push_str(&format!("%{other:02X}")),
}
}
encoded
}
fn decode_component(value: &str) -> Option<String> {
let bytes = value.as_bytes();
let mut decoded = Vec::with_capacity(bytes.len());
let mut index = 0;
while index < bytes.len() {
if bytes[index] == b'%' {
if index + 2 >= bytes.len() {
return None;
}
let hex = std::str::from_utf8(&bytes[index + 1..index + 3]).ok()?;
let byte = u8::from_str_radix(hex, 16).ok()?;
decoded.push(byte);
index += 3;
} else {
decoded.push(bytes[index]);
index += 1;
}
}
String::from_utf8(decoded).ok()
}
#[must_use]
pub fn redis_key(prefix: &str, namespace: &str, collection: &str, key: &str) -> String {
[
prefix,
&encode_component(namespace),
&encode_component(collection),
&encode_component(key),
]
.join(KEY_SEPARATOR)
}
#[must_use]
pub fn redis_prefix(prefix: &str, namespace: &str, collection: &str, key_prefix: &str) -> String {
[
prefix,
&encode_component(namespace),
&encode_component(collection),
&encode_component(key_prefix),
]
.join(KEY_SEPARATOR)
}
#[must_use]
pub fn compose_collection_prefix(prefix: &str, namespace: &str, collection: &str) -> String {
redis_prefix(prefix, namespace, collection, "")
}
#[must_use]
pub fn compose_storage_key(prefix: &str, namespace: &str, collection: &str, key: &str) -> String {
redis_key(prefix, namespace, collection, key)
}
#[must_use]
pub fn decode_redis_key(
prefix: &str,
namespace: &str,
collection: &str,
redis_key: &str,
) -> Option<String> {
let prefix = compose_collection_prefix(prefix, namespace, collection);
let suffix = redis_key.strip_prefix(&prefix)?;
decode_component(suffix)
}