weaver_lib/slugify.rs
1use unicode_normalization::UnicodeNormalization;
2
3/// Converts a heading into a CommonMark/GFM-compatible slug.
4///
5/// Example: `Intro - description` → `intro---description`
6pub fn slugify(input: &str) -> String {
7 let normalized = input.nfkd().collect::<String>().to_lowercase();
8 let mut slug = String::new();
9
10 for c in normalized.chars() {
11 if c.is_alphanumeric() {
12 slug.push(c);
13 } else if c.is_whitespace() || is_gfm_punctuation(c) {
14 slug.push('-');
15 }
16 // skip all other punctuation/symbols
17 }
18
19 // Don't strip or collapse multiple dashes
20 slug
21}
22
23/// Punctuation characters commonly mapped to '-' in GFM
24fn is_gfm_punctuation(c: char) -> bool {
25 matches!(
26 c,
27 '!' | '"'
28 | '#'
29 | '$'
30 | '%'
31 | '&'
32 | '('
33 | ')'
34 | '*'
35 | '+'
36 | ','
37 | '.'
38 | '/'
39 | ':'
40 | ';'
41 | '<'
42 | '='
43 | '>'
44 | '@'
45 | '['
46 | '\\'
47 | ']'
48 | '^'
49 | '_'
50 | '`'
51 | '{'
52 | '|'
53 | '}'
54 | '~'
55 | '-'
56 )
57}