lean_ctx/core/
attribution.rs1pub fn generate_attribution_id(
5 tool: &str,
6 mechanism: &str,
7 session_id: &str,
8 timestamp: &str,
9) -> String {
10 let identity = format!("{tool}|{mechanism}|{session_id}|{timestamp}");
11 let digest = blake3::hash(identity.as_bytes()).to_hex();
12 digest[..16].to_owned()
13}
14
15pub fn check_unique(existing_ids: &[&str], new_id: &str) -> bool {
17 !existing_ids.contains(&new_id)
18}
19
20pub fn attribution_group_for_mechanism(mechanism: &str) -> &'static str {
22 match mechanism {
23 "compression" => "input_optimization",
24 "routing" => "model_selection",
25 "caching" => "cache_discount",
26 _ => "other",
27 }
28}
29
30#[cfg(test)]
31mod tests {
32 use super::{attribution_group_for_mechanism, check_unique, generate_attribution_id};
33
34 #[test]
35 fn generation_is_deterministic() {
36 let first = generate_attribution_id("tool", "compression", "session", "ts");
37 let second = generate_attribution_id("tool", "compression", "session", "ts");
38
39 assert_eq!(first, second);
40 }
41
42 #[test]
43 fn changing_identity_changes_id() {
44 let original = generate_attribution_id("tool", "compression", "session", "ts");
45 let changed = generate_attribution_id("other-tool", "compression", "session", "ts");
46
47 assert_ne!(original, changed);
48 }
49
50 #[test]
51 fn uniqueness_rejects_existing_id() {
52 assert!(check_unique(&["abc", "def"], "ghi"));
53 assert!(!check_unique(&["abc", "def"], "def"));
54 }
55
56 #[test]
57 fn mechanisms_map_to_expected_groups() {
58 assert_eq!(
59 attribution_group_for_mechanism("compression"),
60 "input_optimization"
61 );
62 assert_eq!(
63 attribution_group_for_mechanism("routing"),
64 "model_selection"
65 );
66 assert_eq!(attribution_group_for_mechanism("caching"), "cache_discount");
67 assert_eq!(attribution_group_for_mechanism("other"), "other");
68 }
69
70 #[test]
71 fn generated_id_is_a_16_character_hex_digest() {
72 let id = generate_attribution_id("tool", "routing", "session", "ts");
73
74 assert_eq!(id.len(), 16);
75 assert!(id.bytes().all(|byte| byte.is_ascii_hexdigit()));
76 }
77
78 #[test]
79 fn distinct_records_have_collision_resistant_ids() {
80 let first = generate_attribution_id("tool", "routing", "session-a", "ts");
81 let second = generate_attribution_id("tool", "routing", "session-b", "ts");
82 let third = generate_attribution_id("tool", "routing", "session-a", "ts+1");
83
84 assert_ne!(first, second);
85 assert_ne!(first, third);
86 assert_ne!(second, third);
87 }
88}