pub use crate::service::util::{ clamp_limit_offset, ensure_affected, merge_optional_text, parse_enum, required_text, role_level, }; /// Maximum length for a channel name. pub const MAX_CHANNEL_NAME: usize = 100; /// Maximum length for a channel topic. pub const MAX_CHANNEL_TOPIC: usize = 1024; /// Maximum length for a message body. pub const MAX_MESSAGE_BODY: usize = 4096; /// Maximum length for an article title. pub const MAX_ARTICLE_TITLE: usize = 256; /// Maximum number of poll options. pub const MAX_POLL_OPTIONS: usize = 10; /// Maximum length for a poll option text. pub const MAX_POLL_OPTION_TEXT: usize = 100; /// Redis key prefix for typing indicators. pub const TYPING_PREFIX: &str = "im:typing:"; /// Redis key prefix for user presence. pub const PRESENCE_PREFIX: &str = "im:presence:"; /// Redis TTL for typing indicators (seconds). pub const TYPING_TTL_SECS: usize = 8; /// Redis TTL for presence heartbeats (seconds). pub const PRESENCE_TTL_SECS: usize = 120; /// Maximum length for generated slugs. pub const MAX_SLUG_LEN: usize = 128; /// Generate a slug from a title string. pub fn slugify(title: &str) -> String { let slug: String = title .to_lowercase() .chars() .filter_map(|c| { if c.is_ascii_alphanumeric() { Some(c) } else if c.is_whitespace() || !c.is_ascii() { Some('-') } else { None } }) .collect::() .split('-') .filter(|s| !s.is_empty()) .collect::>() .join("-"); let mut result = slug; result.truncate(MAX_SLUG_LEN); if result.ends_with('-') { result.pop(); } result }