use kube::CustomResource; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; pub fn sanitize_bouncer_name(input: &str) -> String { input .to_lowercase() .chars() .map(|c| if c.is_alphanumeric() || c == '-' { c } else { '-' }) .collect::() .trim_matches('-') .to_string() } // Minimal SojuBouncer spec -- just enough for the portal to create CRs. // The full CRD is defined in the soju-operator crate. #[derive(CustomResource, Debug, Clone, Serialize, Deserialize, JsonSchema)] #[kube( group = "irc.now", version = "v1alpha1", kind = "SojuBouncer", namespaced )] pub struct SojuBouncerSpec { pub hostname: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub owner: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub hostname_cloak: Option, } #[cfg(test)] mod tests { use super::*; #[test] fn bouncer_name_sanitizes_input() { assert_eq!(sanitize_bouncer_name("My Bouncer!"), "my-bouncer"); assert_eq!(sanitize_bouncer_name("test_123"), "test-123"); } #[test] fn bouncer_name_trims_dashes() { assert_eq!(sanitize_bouncer_name("--hello--"), "hello"); } #[test] fn bouncer_name_handles_empty() { assert_eq!(sanitize_bouncer_name(""), ""); } }