Files
kompress_eshop/src/shared/slug.rs
Priec 9ce07e8c23
Some checks failed
CI / Check Style (push) Has been cancelled
CI / Run Clippy (push) Has been cancelled
CI / Run Tests (push) Has been cancelled
better project structure
2026-06-16 22:48:10 +02:00

39 lines
1.2 KiB
Rust

//! URL slug helpers shared by the catalog admin (products and categories).
use loco_rs::prelude::*;
/// Lowercase a string and collapse every run of non-alphanumeric characters
/// into a single dash, trimming dashes from the ends.
pub fn slugify(value: &str) -> String {
let mut slug = String::new();
let mut last_was_dash = false;
for ch in value.chars().flat_map(char::to_lowercase) {
if ch.is_ascii_alphanumeric() {
slug.push(ch);
last_was_dash = false;
} else if !last_was_dash && !slug.is_empty() {
slug.push('-');
last_was_dash = true;
}
}
slug.trim_matches('-').to_string()
}
/// Find the first slug that does not already exist, appending `-2`, `-3`, … to
/// `base` until `exists` reports the candidate as free. An empty `base` falls
/// back to `"item"`.
pub async fn unique_slug<F, Fut>(base: &str, mut exists: F) -> Result<String>
where
F: FnMut(String) -> Fut,
Fut: std::future::Future<Output = Result<bool>>,
{
let base = if base.is_empty() { "item" } else { base };
let mut slug = base.to_string();
let mut suffix = 2;
while exists(slug.clone()).await? {
slug = format!("{base}-{suffix}");
suffix += 1;
}
Ok(slug)
}