1pub mod brain;
2pub mod config;
3pub mod events;
4pub mod github;
5pub mod hooks;
6pub mod lifecycle;
7pub mod plugin;
8pub mod pty;
9pub mod store;
10pub mod tmux;
11pub mod types;
12
13pub use brain::{BrainEntry, BrainIndex, QueryFilters};
14pub use config::{AppConfig, ThemeVariant};
15pub use events::{Engine, Event};
16pub use store::Store;
17pub use types::*;
18
19pub fn slugify(s: &str) -> String {
26 s.to_lowercase()
27 .chars()
28 .map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })
29 .collect::<String>()
30 .split('-')
31 .filter(|p| !p.is_empty())
32 .collect::<Vec<_>>()
33 .join("-")
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 #[test]
41 fn slugify_ticket_id() {
42 assert_eq!(slugify("ATH-123"), "ath-123");
43 }
44
45 #[test]
46 fn slugify_spaced_name() {
47 assert_eq!(slugify("auth fix"), "auth-fix");
48 }
49
50 #[test]
51 fn slugify_ticket_with_description() {
52 assert_eq!(slugify("ATH-123 auth fix"), "ath-123-auth-fix");
53 }
54
55 #[test]
56 fn slugify_collapses_consecutive_separators() {
57 assert_eq!(slugify("my feature!"), "my-feature");
58 }
59
60 #[test]
61 fn slugify_already_clean() {
62 assert_eq!(slugify("my-feature"), "my-feature");
63 }
64
65 #[test]
66 fn slugify_empty_returns_empty() {
67 assert_eq!(slugify(""), "");
68 assert_eq!(slugify("---"), "");
69 }
70}