Skip to main content

textlane_core/helpers/
slug.rs

1use std::collections::HashMap;
2
3use crate::helpers::slug_table::REMOVED;
4
5/// github-slugger's `BananaSlug`: slugs, made unique by counting repeats.
6#[derive(Debug, Default)]
7pub struct Slugger {
8    occurrences: HashMap<String, usize>,
9}
10
11impl Slugger {
12    /// A unique slug for `value`: `a`, then `a-1`, `a-2`.
13    pub fn slug(&mut self, value: &str) -> String {
14        let original = create_slug(value);
15        let mut result = original.clone();
16        while self.occurrences.contains_key(&result) {
17            let count = self.occurrences.entry(original.clone()).or_default();
18            *count += 1;
19            result = format!("{original}-{count}");
20        }
21        self.occurrences.insert(result.clone(), 0);
22        result
23    }
24}
25
26/// Lowercase, drop punctuation and symbols, turn spaces into dashes.
27pub fn create_slug(value: &str) -> String {
28    value
29        .to_lowercase()
30        .chars()
31        .filter(|character| !is_removed(*character))
32        .map(|character| if character == ' ' { '-' } else { character })
33        .collect()
34}
35
36fn is_removed(character: char) -> bool {
37    let code = u32::from(character);
38    REMOVED
39        .binary_search_by(|(start, end)| {
40            if *end < code {
41                std::cmp::Ordering::Less
42            } else if *start > code {
43                std::cmp::Ordering::Greater
44            } else {
45                std::cmp::Ordering::Equal
46            }
47        })
48        .is_ok()
49}