use crate::index::AnnotationId;
const READABLE_TARGET_LEN: usize = 32;
const OPAQUE_BODY_LEN: usize = 8;
const OPAQUE_ALPHABET: &[u8] = b"abcdefghjkmnpqrstuvwxyz23456789";
pub fn snake_case_from_text(text: &str) -> Option<String> {
let mut out = String::new();
let mut last_was_underscore = true;
for ch in text.chars() {
if out.len() >= READABLE_TARGET_LEN {
break;
}
if ch.is_ascii_alphanumeric() {
out.push(ch.to_ascii_lowercase());
last_was_underscore = false;
} else if !last_was_underscore {
out.push('_');
last_was_underscore = true;
}
}
while out.ends_with('_') {
out.pop();
}
if out.is_empty() {
return None;
}
if !out.starts_with(|c: char| c.is_ascii_lowercase() || c == '_') {
out.insert_str(0, "n_");
}
Some(out)
}
#[aristo::intent(
"Opaque ids carry enough entropy that collisions across a project are \
negligible. If the OS can't produce randomness, the stamp crashes; a \
low-entropy id silently committed would be worse than a failed run \
the user can retry.",
verify = "neural",
id = "generate_opaque_id_always_parses"
)]
pub fn generate_opaque_id() -> AnnotationId {
let mut bytes = [0u8; OPAQUE_BODY_LEN];
getrandom::getrandom(&mut bytes)
.expect("OS RNG failed; aborting rather than emitting a low-entropy id");
let mut s = String::with_capacity("aret_".len() + OPAQUE_BODY_LEN);
s.push_str("aret_");
for byte in bytes {
let idx = (byte as usize) % OPAQUE_ALPHABET.len();
s.push(OPAQUE_ALPHABET[idx] as char);
}
AnnotationId::parse(&s).expect("opaque-id alphabet stays inside aret_ namespace charset")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extracts_readable_id_from_typical_intent_text() {
let id = snake_case_from_text("the function returns the input plus one")
.expect("readable id should derive from normal English");
assert!(id.starts_with("the_function_returns_the"), "got: {id}");
assert!(id.len() <= READABLE_TARGET_LEN);
}
#[test]
fn collapses_runs_of_non_alphanumeric_into_single_underscore() {
let id = snake_case_from_text("hello, world!! how are you?").unwrap();
assert_eq!(id, "hello_world_how_are_you");
}
#[test]
fn lowercases_uppercase_input() {
let id = snake_case_from_text("Balance NoDuplicateCells").unwrap();
assert_eq!(id, "balance_noduplicatecells");
}
#[test]
fn caps_at_target_length() {
let long = "a".repeat(200);
let id = snake_case_from_text(&long).unwrap();
assert!(id.len() <= READABLE_TARGET_LEN, "got len {}", id.len());
}
#[test]
fn returns_none_for_empty_input() {
assert_eq!(snake_case_from_text(""), None);
}
#[test]
fn returns_none_for_punctuation_only() {
assert_eq!(snake_case_from_text("!!! ??? ..."), None);
}
#[test]
fn returns_none_for_non_ascii_only() {
assert_eq!(snake_case_from_text("你好世界"), None);
}
#[test]
fn prepends_n_underscore_when_text_starts_with_digit() {
let id = snake_case_from_text("42 is the answer").unwrap();
assert!(id.starts_with("n_"), "got: {id}");
AnnotationId::parse(&id).expect("output should be a valid local AnnotationId");
}
#[test]
fn output_is_a_valid_annotation_id() {
for input in [
"the function returns x",
"Balance no duplicate cells",
"BTreeMap maintains sort order",
" whitespace trimmed ",
"weird,,,,,punctuation:::",
"42 starts with digit",
] {
let id = snake_case_from_text(input).unwrap();
AnnotationId::parse(&id)
.unwrap_or_else(|e| panic!("input {input:?} → {id:?} failed parse: {e}"));
}
}
#[test]
fn opaque_id_has_aret_prefix() {
let id = generate_opaque_id();
assert!(id.as_str().starts_with("aret_"), "got: {id}");
}
#[test]
fn opaque_id_has_expected_length() {
let id = generate_opaque_id();
assert_eq!(id.as_str().len(), "aret_".len() + OPAQUE_BODY_LEN);
}
#[test]
fn opaque_id_uses_safe_alphabet_only() {
let id = generate_opaque_id();
let body = id.as_str().strip_prefix("aret_").unwrap();
for ch in body.chars() {
assert!(
OPAQUE_ALPHABET.contains(&(ch as u8)),
"opaque id contains char `{ch}` not in alphabet"
);
}
}
#[test]
fn opaque_ids_are_distinct_with_high_probability() {
let mut seen = std::collections::HashSet::new();
for _ in 0..1000 {
let id = generate_opaque_id();
assert!(
seen.insert(id.as_str().to_owned()),
"duplicate opaque id within 1000 draws — RNG is broken"
);
}
}
}