ddapi_rs/util/
encoding.rs

1use std::borrow::Cow;
2use std::fmt::Write;
3
4const SLUGIFY2_SYMBOLS: &str = "[\t !\"#$%&'()*-/<=>?@[\\]^_`{|},.:]+";
5const NON_ASCII_CHARACTER_THRESHOLD: u32 = 128;
6
7/// Converts a nickname to a URL-safe slug format for API requests
8///
9/// This function handles special characters and non-ASCII characters in nicknames
10/// by encoding them into a format that can be safely used in URLs. Characters that
11/// are not ASCII or are in `SLUGIFY_SYMBOLS` are converted to their Unicode code points
12/// surrounded by hyphens.
13///
14/// # Arguments
15///
16/// * `nickname` - The player nickname to slugify
17///
18/// # Returns
19///
20/// Returns `Cow<'_, str>` - Borrowed if no conversion needed, Owned if conversion occurred
21///
22/// # Examples
23///
24/// ```
25/// use ddapi_rs::prelude::slugify2;
26///
27/// // ASCII-only nicknames without special symbols are returned as-is
28/// assert_eq!(slugify2("Player1"), "Player1");
29///
30/// // Special symbols and non-ASCII characters are encoded
31/// assert_eq!(slugify2("Player@"), "Player-64-");
32/// assert_eq!(slugify2("玩家"), "-29609--23478-");
33///
34/// // Mixed characters
35/// assert_eq!(slugify2("Test_Player"), "Test-95-Player");
36/// ```
37pub fn slugify2(nickname: &str) -> Cow<'_, str> {
38    let needs_processing = nickname
39        .chars()
40        .any(|c| SLUGIFY2_SYMBOLS.contains(c) || (c as u32) >= NON_ASCII_CHARACTER_THRESHOLD);
41
42    if !needs_processing {
43        return Cow::Borrowed(nickname);
44    }
45
46    let mut result = String::with_capacity(nickname.len() * 4);
47
48    for c in nickname.chars() {
49        if SLUGIFY2_SYMBOLS.contains(c) || (c as u32) >= NON_ASCII_CHARACTER_THRESHOLD {
50            write!(&mut result, "-{}-", c as u32).unwrap();
51        } else {
52            result.push(c);
53        }
54    }
55
56    Cow::Owned(result)
57}
58
59/// Encodes a nickname for safe use in URLs
60///
61/// This function ensures that nicknames containing special characters, spaces,
62/// or non-ASCII characters are properly URL-encoded. ASCII nicknames without
63/// control characters are returned as-is for better performance.
64///
65/// # Arguments
66///
67/// * `nickname` - The player nickname to URL-encode
68///
69/// # Returns
70///
71/// Returns `Cow<'_, str>` -
72/// - `Cow::Borrowed` if the nickname is already URL-safe (ASCII without control characters)
73/// - `Cow::Owned` with URL-encoded string if encoding is required
74///
75/// # Examples
76///
77/// ```
78/// use ddapi_rs::prelude::encode;
79///
80/// // Safe ASCII nicknames are returned without changes
81/// assert_eq!(encode("Player1"), "Player1");
82/// assert_eq!(encode("abc_XYZ"), "abc_XYZ");
83///
84/// // Characters requiring encoding are properly handled
85/// assert_eq!(encode("Player Server"), "Player%20Server");
86/// assert_eq!(encode("Player@Server"), "Player%40Server");
87/// assert_eq!(encode("玩家"), "%E7%8E%A9%E5%AE%B6");
88/// assert_eq!(encode("emoji🎮"), "emoji%F0%9F%8E%AE");
89///
90/// // Special cases
91/// assert_eq!(encode(""), "");
92/// assert_eq!(encode("a b"), "a%20b");
93/// ```
94pub fn encode(nickname: &str) -> Cow<'_, str> {
95    if nickname
96        .chars()
97        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' || c == '~')
98    {
99        Cow::Borrowed(nickname)
100    } else {
101        urlencoding::encode(nickname)
102    }
103}