Skip to main content

truce_utils/
lib.rs

1#![forbid(unsafe_code)]
2
3//! Dependency-free utilities shared across the truce workspace.
4//!
5//! Two unrelated families of helpers live here so neither has to pull in
6//! a heavier crate's transitive chain:
7//!
8//! - [`cast`] — numeric-cast helpers for the audio-plugin → host FFI
9//!   boundary (MIDI byte encodes, `usize` ↔ `u32` length casts, host
10//!   `f64` ↔ DSP `f32`, discrete-index ↔ normalized).
11//! - [`slugify`] — ASCII-safe filesystem / IRI slug used by the LV2
12//!   staging path and runtime bundle-name derivation.
13//!
14//! `truce-core` re-exports the [`cast`] module and [`slugify`] for
15//! backwards compatibility with workspace call sites that already
16//! import `truce_core::cast::*` and `truce_core::slugify`. Crates that
17//! want to avoid `truce-core`'s `truce-params` chain (notably
18//! `cargo-truce`) depend on `truce-utils` directly.
19
20pub mod cast;
21
22/// Slug a plugin's display name into a lowercase, hyphenated,
23/// ASCII-safe identifier suitable for filesystem paths, LV2 bundle
24/// names, and IRI components.
25///
26/// Rules: ASCII alphanumerics pass through lowercased; every other
27/// character (including runs of them) collapses to a single `-`;
28/// leading and trailing dashes are trimmed.
29#[must_use]
30pub fn slugify(name: &str) -> String {
31    let mut out = String::with_capacity(name.len());
32    let mut prev_dash = false;
33    for c in name.chars() {
34        if c.is_ascii_alphanumeric() {
35            out.push(c.to_ascii_lowercase());
36            prev_dash = false;
37        } else if !prev_dash {
38            out.push('-');
39            prev_dash = true;
40        }
41    }
42    out.trim_matches('-').to_string()
43}
44
45#[cfg(test)]
46mod slugify_tests {
47    use super::slugify;
48
49    #[test]
50    fn slugify_basic() {
51        assert_eq!(slugify("My Plugin"), "my-plugin");
52        assert_eq!(slugify("Hello!! World"), "hello-world");
53        assert_eq!(slugify("--leading and trailing--"), "leading-and-trailing");
54        assert_eq!(slugify("ABC123"), "abc123");
55        assert_eq!(slugify(""), "");
56    }
57}