solti_model/domain/identity/
slot.rs1use super::validate_identity;
7use crate::error::ModelError;
8
9pub const SLOT_MAX_LEN: usize = 64;
11
12arc_str_newtype! {
13 #[cfg_attr(feature = "schema", schemars(schema_with = "crate::schema::slot"))]
14 pub struct Slot;
29}
30
31impl Slot {
32 pub fn validate_format(&self) -> Result<(), ModelError> {
47 validate_identity("slot", self.as_str(), SLOT_MAX_LEN)
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::Slot;
54 use std::sync::Arc;
55
56 #[test]
57 fn exposes_string_conversions_and_shares_clones() {
58 let slot = Slot::new("shared").unwrap();
59 let parsed: Slot = "shared".parse().unwrap();
60
61 assert_eq!(slot.as_str(), "shared");
62 assert_eq!(format!("{slot}"), "shared");
63 assert_eq!(slot, *"shared");
64 assert_eq!(slot, parsed);
65
66 let cloned = slot.clone();
67 let a: Arc<str> = slot.into_inner();
68 let b: Arc<str> = cloned.into_inner();
69 assert_eq!(&*a, "shared");
70 assert!(Arc::ptr_eq(&a, &b));
71 }
72
73 #[test]
74 fn serde_is_transparent() {
75 let slot = Slot::new("build").unwrap();
76 let json = serde_json::to_string(&slot).unwrap();
77 assert_eq!(json, "\"build\"");
78 assert_eq!(serde_json::from_str::<Slot>(&json).unwrap(), slot);
79 }
80
81 #[test]
82 fn validation_accepts_safe_values_and_rejects_unsafe_values() {
83 for valid in ["build.frontend", "build", "a"] {
84 Slot::new(valid).unwrap();
85 }
86 for invalid in ["build/frontend", "é", "with space", "a\nb", ".", ""] {
87 assert!(Slot::new(invalid).is_err(), "must reject {invalid:?}");
88 }
89 }
90}