Skip to main content

wipe_core/
id.rs

1//! Stable, human-friendly ID formatting.
2//!
3//! Ticket IDs look like `T-23`, comment IDs like `c-7`. IDs are allocated from
4//! monotonic counters stored in the board / ticket, so they are deterministic and
5//! never reused within a board.
6
7/// Format a ticket ID from its numeric counter, e.g. `T-23`.
8pub fn ticket_id(n: u64) -> String {
9    format!("T-{n}")
10}
11
12/// Format a comment ID from its numeric counter, e.g. `c-7`.
13pub fn comment_id(n: u64) -> String {
14    format!("c-{n}")
15}
16
17/// Turn a human name into a stable kebab-case slug used for list IDs.
18///
19/// Non-alphanumeric runs collapse to a single `-`; the result is lowercased and
20/// trimmed of leading/trailing dashes. Empty input yields `"list"`.
21pub fn slug(name: &str) -> String {
22    let mut out = String::with_capacity(name.len());
23    let mut prev_dash = false;
24    for ch in name.chars() {
25        if ch.is_ascii_alphanumeric() {
26            out.push(ch.to_ascii_lowercase());
27            prev_dash = false;
28        } else if !prev_dash {
29            out.push('-');
30            prev_dash = true;
31        }
32    }
33    let trimmed = out.trim_matches('-');
34    if trimmed.is_empty() {
35        "list".to_string()
36    } else {
37        trimmed.to_string()
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn formats_ids() {
47        assert_eq!(ticket_id(23), "T-23");
48        assert_eq!(comment_id(7), "c-7");
49    }
50
51    #[test]
52    fn slugs_names() {
53        assert_eq!(slug("In Progress"), "in-progress");
54        assert_eq!(slug("  To-Do!! "), "to-do");
55        assert_eq!(slug("Backlog"), "backlog");
56        assert_eq!(slug("***"), "list");
57    }
58}