use crate::Dialect;
use std::fmt::Write;
const PREFIXES: [&str; 2] = ["toolu_", "call_"];
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ToolAlias {
Upstream { id: String, repeat: u64 },
Missing {
legacy: bool,
salt: String,
index: u64,
},
}
pub fn accepts(dialect: Dialect, id: &str) -> bool {
!id.is_empty()
&& (dialect != Dialect::Claude
|| id
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-'))
}
pub fn upstream(prefix: &str, id: &str, repeat: u64) -> String {
let mut alias = format!("{prefix}gpe");
if repeat > 0 {
let _ = write!(alias, "{repeat}");
}
alias.push('_');
for byte in id.bytes() {
match byte {
b'_' => alias.push_str("__"),
b if b.is_ascii_alphanumeric() || b == b'-' => alias.push(char::from(b)),
b => {
let _ = write!(alias, "_{b:02x}");
}
}
}
alias
}
pub fn missing(prefix: &str, salt: &str, index: u64, legacy: bool) -> String {
let tag = if legacy { 'l' } else { 'n' };
format!("{prefix}gp{tag}_{salt}_{index}")
}
pub fn decode(alias: &str) -> Option<ToolAlias> {
let rest = PREFIXES
.iter()
.find_map(|prefix| alias.strip_prefix(prefix))?
.strip_prefix("gp")?;
let (tag, rest) = rest.split_at_checked(1)?;
let (count, body) = rest.split_once('_')?;
match tag {
"e" => {
let repeat = if count.is_empty() {
0
} else {
canonical_number(count).filter(|n| *n > 0)?
};
Some(ToolAlias::Upstream {
id: unescape(body)?,
repeat,
})
}
"n" | "l" if count.is_empty() => {
let (salt, index) = body.split_once('_')?;
if salt.is_empty()
|| !salt
.bytes()
.all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
{
return None;
}
Some(ToolAlias::Missing {
legacy: tag == "l",
salt: salt.to_owned(),
index: canonical_number(index)?,
})
}
_ => None,
}
}
fn canonical_number(value: &str) -> Option<u64> {
if value.is_empty() || !value.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
if value.len() > 1 && value.starts_with('0') {
return None;
}
value.parse().ok()
}
fn unescape(body: &str) -> Option<String> {
if body.is_empty() {
return None;
}
let mut bytes = Vec::with_capacity(body.len());
let mut input = body.bytes();
while let Some(byte) = input.next() {
match byte {
b'_' => match input.next()? {
b'_' => bytes.push(b'_'),
high => {
let byte = (hex(high)? << 4) | hex(input.next()?)?;
if byte == b'_' || byte == b'-' || byte.is_ascii_alphanumeric() {
return None;
}
bytes.push(byte);
}
},
b if b.is_ascii_alphanumeric() || b == b'-' => bytes.push(b),
_ => return None,
}
}
String::from_utf8(bytes).ok()
}
fn hex(digit: u8) -> Option<u8> {
match digit {
b'0'..=b'9' => Some(digit - b'0'),
b'a'..=b'f' => Some(digit - b'a' + 10),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn up(id: &str, repeat: u64) -> Option<ToolAlias> {
Some(ToolAlias::Upstream {
id: id.into(),
repeat,
})
}
#[test]
fn escaped_ids_round_trip_through_the_claude_alphabet() {
for id in [
"a.b",
"tool:source",
"plain",
"under_score",
"__",
"_x2e",
"-",
"ü/日本",
" ",
"%",
"toolu_gpe_x",
] {
for repeat in [0, 1, 12] {
let alias = upstream("toolu_", id, repeat);
assert!(accepts(Dialect::Claude, &alias), "{alias}");
assert_eq!(decode(&alias), up(id, repeat), "{alias}");
}
}
assert_eq!(upstream("toolu_", "a.b", 0), "toolu_gpe_a_2eb");
assert_eq!(upstream("call_", "a_b", 2), "call_gpe2_a__b");
assert_eq!(
upstream("toolu_", "tool:source", 0),
"toolu_gpe_tool_3asource"
);
}
#[test]
fn missing_ids_carry_their_form_and_position() {
let alias = missing("toolu_", "00ff", 3, false);
assert_eq!(alias, "toolu_gpn_00ff_3");
assert!(accepts(Dialect::Claude, &alias));
assert_eq!(
decode(&alias),
Some(ToolAlias::Missing {
legacy: false,
salt: "00ff".into(),
index: 3
})
);
assert_eq!(
decode(&missing("call_", "ab", 0, true)),
Some(ToolAlias::Missing {
legacy: true,
salt: "ab".into(),
index: 0
})
);
}
#[test]
fn natural_ids_do_not_decode() {
for id in [
"",
"toolu_01AbCdEf",
"call_abc123",
"call_gp",
"call_gpe",
"call_gpe_",
"toolu_gpx_a",
"gpe_abc",
"fc_gpe_abc",
"call_gpe0_a",
"call_gpe01_a",
"call_gpe_a_",
"call_gpe_a_2",
"call_gpe_a_2E",
"call_gpe_a_41",
"call_gpe_a_5f",
"call_gpe_a.b",
"call_gpe_a_ff",
"call_gpn_ab",
"call_gpn__1",
"call_gpn_AB_1",
"call_gpn_ab_01",
"call_gpn_ab_x",
"call_gpn1_ab_1",
"call_gpl_ab_1_2",
] {
assert_eq!(decode(id), None, "{id}");
}
}
#[test]
fn an_id_that_looks_like_an_alias_is_escaped_apart_from_it() {
let natural = "call_gpe_x";
assert_eq!(decode(natural), up("x", 0));
let alias = upstream("call_", natural, 0);
assert_ne!(alias, natural);
assert_eq!(decode(&alias), up(natural, 0));
let missing = missing("call_", "ab", 0, false);
assert_eq!(decode(&upstream("call_", &missing, 0)), up(&missing, 0));
}
#[test]
fn only_claude_restricts_the_alphabet() {
assert!(!accepts(Dialect::Claude, "a.b"));
assert!(!accepts(Dialect::Claude, ""));
assert!(accepts(Dialect::Claude, "toolu_01-x_Y"));
assert!(accepts(Dialect::OpenAiChat, "a.b:c"));
assert!(accepts(Dialect::Gemini, "a b"));
assert!(!accepts(Dialect::OpenAi, ""));
}
}