use crate::error::{Error, Result};
pub const PREFIX_SEPARATOR: char = '.';
pub fn join_prefix(prefix: &str, name: &str) -> Result<String> {
let prefix = prefix.trim();
let name = name.trim();
if prefix.is_empty() || name.is_empty() {
return Err(Error::Naming(
"prefix and name must be non-empty after trim".into(),
));
}
if prefix.contains(PREFIX_SEPARATOR) || name.contains(PREFIX_SEPARATOR) {
return Err(Error::Naming(
"prefix and name must not contain the separator '.'".into(),
));
}
let mut out = String::with_capacity(prefix.len() + 1 + name.len());
out.push_str(prefix);
out.push(PREFIX_SEPARATOR);
out.push_str(name);
Ok(out)
}
pub fn split_prefixed(account: &str) -> Result<(String, String)> {
let account = account.trim();
let (p, n) = account.split_once(PREFIX_SEPARATOR).ok_or_else(|| {
Error::Naming("account must contain exactly one '.' between prefix and name".into())
})?;
if p.is_empty() || n.is_empty() {
return Err(Error::Naming(
"prefix and name segments must be non-empty".into(),
));
}
if n.contains(PREFIX_SEPARATOR) {
return Err(Error::Naming(
"only one separator allowed use nested naming in the app layer".into(),
));
}
Ok((p.to_string(), n.to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn join_ok() {
assert_eq!(join_prefix("app", "token").unwrap(), "app.token");
}
#[test]
fn join_rejects_extra_dot() {
assert!(join_prefix("a.b", "c").is_err());
}
#[test]
fn split_roundtrip() {
let s = join_prefix("my", "key").unwrap();
assert_eq!(split_prefixed(&s).unwrap(), ("my".into(), "key".into()));
}
}