use std::borrow::Cow;
const MARKER: &str = "mk--";
fn is_valid_comms_component(s: &str) -> bool {
let mut chars = s.chars();
let Some(first) = chars.next() else {
return false;
};
if !first.is_ascii_alphabetic() && first != '_' {
return false;
}
chars.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
}
fn escape_body(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 8);
for c in s.chars() {
match c {
'_' => out.push_str("__"),
':' => out.push_str("_c"),
c if c.is_ascii_alphanumeric() || c == '-' => out.push(c),
c => {
out.push_str("_x");
out.push_str(&format!("{:x}", c as u32));
out.push('_');
}
}
}
out
}
fn unescape_body(s: &str) -> Option<String> {
let mut out = String::with_capacity(s.len());
let mut chars = s.chars();
while let Some(c) = chars.next() {
if c != '_' {
out.push(c);
continue;
}
match chars.next()? {
'_' => out.push('_'),
'c' => out.push(':'),
'x' => {
let mut hex = String::new();
loop {
match chars.next()? {
'_' => break,
h => hex.push(h),
}
}
let code = u32::from_str_radix(&hex, 16).ok()?;
out.push(char::from_u32(code)?);
}
_ => return None,
}
}
Some(out)
}
pub fn mob_member_id_str(alias: &str) -> Cow<'_, str> {
if is_valid_comms_component(alias) && !alias.starts_with(MARKER) {
Cow::Borrowed(alias)
} else {
Cow::Owned(format!("{MARKER}{}", escape_body(alias)))
}
}
pub fn mob_member_id(alias: &str) -> meerkat_mob::ids::AgentIdentity {
meerkat_mob::ids::AgentIdentity::from(mob_member_id_str(alias).as_ref())
}
pub fn runtime_alias_str(member_id: &str) -> Cow<'_, str> {
match member_id.strip_prefix(MARKER) {
Some(body) => match unescape_body(body) {
Some(alias) => Cow::Owned(alias),
None => Cow::Borrowed(member_id),
},
None => Cow::Borrowed(member_id),
}
}
pub fn runtime_event_alias(runtime_id: &meerkat_mob::ids::AgentRuntimeId) -> String {
format!(
"{}:{}",
runtime_alias_str(runtime_id.identity.as_str()),
runtime_id.generation.get()
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn plain_member_names_pass_through_unchanged() {
for name in ["worker", "worker-one", "_internal", "Agent7"] {
assert_eq!(mob_member_id_str(name), name);
assert_eq!(runtime_alias_str(name), name);
}
}
#[test]
fn colon_aliases_round_trip_and_are_comms_safe() {
for alias in [
"rt:review:singleton:0",
"rt:channel:C0SMOKEOB3:0",
"agent:beta",
"review:singleton",
"rt:agent-x:0",
"with_underscore:and:colons",
] {
let encoded = mob_member_id_str(alias);
assert!(
is_valid_comms_component(&encoded),
"{encoded:?} must satisfy meerkat 0.7 MemberCommsName"
);
assert_eq!(runtime_alias_str(&encoded), alias, "round trip of {alias}");
}
}
#[test]
fn non_ascii_and_punctuation_round_trip() {
for alias in ["user@host", "a.b:c", "9starts-with-digit", "ünïcode:1"] {
let encoded = mob_member_id_str(alias);
assert!(is_valid_comms_component(&encoded), "{encoded:?}");
assert_eq!(runtime_alias_str(&encoded), alias);
}
}
#[test]
fn reserved_marker_names_re_encode_so_round_trip_holds() {
let alias = "mk--rt_creview";
let encoded = mob_member_id_str(alias);
assert_ne!(encoded, alias, "marker-prefixed names must be re-encoded");
assert_eq!(runtime_alias_str(&encoded), alias);
}
#[test]
fn runtime_event_alias_decodes_encoded_roster_member_ids() {
use meerkat_mob::ids::{AgentIdentity, AgentRuntimeId, Generation};
let encoded = mob_member_id_str("rt:review:singleton:0").into_owned();
let runtime_id =
AgentRuntimeId::new(AgentIdentity::from(encoded.as_str()), Generation::new(1));
assert_eq!(runtime_event_alias(&runtime_id), "rt:review:singleton:0:1");
let runtime_id = AgentRuntimeId::initial(AgentIdentity::from("worker-one"));
assert_eq!(runtime_event_alias(&runtime_id), "worker-one:0");
}
#[test]
fn encoding_is_injective_across_pass_through_and_encoded_forms() {
let inputs = [
"rt:review:singleton:0",
"rt-review-singleton-0",
"rt_creview_csingleton_c0",
"mk--rt_creview_csingleton_c0",
];
let mut seen = std::collections::BTreeSet::new();
for input in inputs {
assert!(
seen.insert(mob_member_id_str(input).into_owned()),
"collision on {input}"
);
}
}
}