Skip to main content

api_scanner/scanner/common/
string_utils.rs

1pub fn redact_secret(s: &str, keep_chars: usize) -> String {
2    let chars: Vec<char> = s.chars().collect();
3    if chars.len() <= keep_chars * 2 {
4        return "*".repeat(chars.len());
5    }
6
7    let head: String = chars[..keep_chars].iter().collect();
8    let tail: String = chars[chars.len() - keep_chars..].iter().collect();
9    let stars = "*".repeat(chars.len().saturating_sub(keep_chars * 2).min(12));
10    format!("{head}{stars}{tail}")
11}
12
13pub fn snippet(s: &str, max_len: usize) -> String {
14    if s.len() <= max_len {
15        s.to_string()
16    } else {
17        let boundary = s
18            .char_indices()
19            .take_while(|&(i, _)| i < max_len)
20            .last()
21            .map(|(i, c)| i + c.len_utf8())
22            .unwrap_or(0);
23        format!("{}... ({} bytes total)", &s[..boundary], s.len())
24    }
25}
26
27pub fn slugify(s: &str) -> String {
28    let mut result = String::with_capacity(s.len());
29    let mut prev_dash = false;
30
31    for c in s.chars() {
32        if c.is_ascii_alphanumeric() {
33            result.push(c.to_ascii_lowercase());
34            prev_dash = false;
35        } else if !prev_dash {
36            result.push('-');
37            prev_dash = true;
38        }
39    }
40
41    // Trim leading/trailing dashes
42    result.trim_matches('-').to_string()
43}