steam_friend_code/
short_steam_friend_code.rs1pub const SHORT_STEAM_FRIEND_CODE_CHARS: [char; 16] = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 't', 'v', 'w'];
9
10pub fn create_short_steam_friend_code(account_id: u32) -> String {
22 let hex = format!("{:x}", account_id);
23
24 let mut friend_code = String::with_capacity(hex.len() + 1);
25
26 for c in hex.chars() {
27 let digit = c.to_digit(16).unwrap_or(0) as usize;
28 friend_code.push(SHORT_STEAM_FRIEND_CODE_CHARS[digit]);
29 }
30
31 let dash_pos = friend_code.len() / 2;
33 if dash_pos > 0 && friend_code.len() > 1 {
34 friend_code.insert(dash_pos, '-');
35 }
36
37 friend_code
38}
39
40pub fn parse_short_steam_friend_code(friend_code: &str) -> Option<u32> {
44 let code = friend_code.replace('-', "");
45 let mut hex = String::with_capacity(code.len());
46
47 for c in code.chars() {
48 let pos = SHORT_STEAM_FRIEND_CODE_CHARS.iter().position(|&x| x == c)?;
49 hex.push(char::from_digit(pos as u32, 16).unwrap());
50 }
51
52 u32::from_str_radix(&hex, 16).ok()
53}
54
55pub fn parse_quick_invite_link(link: &str) -> Option<(String, String)> {
64 let path = link.trim().trim_start_matches("https://").trim_start_matches("http://").trim_start_matches("s.team/p/").trim_start_matches("steamcommunity.com/user/");
65
66 let parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
67
68 if parts.len() < 2 {
69 return None;
70 }
71
72 Some((parts[0].to_string(), parts[1].to_string()))
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_create_short_steam_friend_code() {
81 let code = create_short_steam_friend_code(123456789);
82 for c in code.chars() {
83 assert!(SHORT_STEAM_FRIEND_CODE_CHARS.contains(&c) || c == '-');
84 }
85 assert_eq!(code.matches('-').count(), 1);
86 }
87
88 #[test]
89 fn test_parse_short_steam_friend_code_roundtrip() {
90 let ids = [123456789u32, 1, 999999, 2147483647];
91 for id in ids {
92 let code = create_short_steam_friend_code(id);
93 let parsed = parse_short_steam_friend_code(&code);
94 assert_eq!(parsed, Some(id), "Roundtrip failed for id {} code {}", id, code);
95 }
96 }
97
98 #[test]
99 fn test_parse_quick_invite_link() {
100 let (code, token) = parse_quick_invite_link("https://s.team/p/bcdf-ghjk/ABCD1234").unwrap();
101 assert_eq!(code, "bcdf-ghjk");
102 assert_eq!(token, "ABCD1234");
103
104 let (code, token) = parse_quick_invite_link("bcdf-ghjk/TOKEN").unwrap();
105 assert_eq!(code, "bcdf-ghjk");
106 assert_eq!(token, "TOKEN");
107 }
108
109 #[test]
110 fn test_parse_quick_invite_link_invalid() {
111 assert!(parse_quick_invite_link("https://s.team/p/bcdf-ghjk").is_none());
112 assert!(parse_quick_invite_link("").is_none());
113 }
114}