1use serde::{Deserialize, Serialize};
10
11pub mod request;
12pub mod response;
13
14pub const PROTOCOL_V3: &str = "3.0";
15
16#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
29pub struct Cohort {
30 #[serde(rename = "cohort")]
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub id: Option<String>,
34
35 #[serde(rename = "cohorthint")]
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub hint: Option<String>,
38
39 #[serde(rename = "cohortname")]
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub name: Option<String>,
42}
43
44impl Cohort {
45 pub fn new(id: &str) -> Cohort {
47 Cohort { id: Some(id.to_string()), hint: None, name: None }
48 }
49
50 pub fn from_hint(hint: &str) -> Cohort {
51 Cohort { id: None, hint: Some(hint.to_string()), name: None }
52 }
53
54 pub fn update_from_omaha(&mut self, omaha_cohort: Self) {
55 if omaha_cohort.id.is_some() {
59 self.id = omaha_cohort.id;
60 }
61 if omaha_cohort.hint.is_some() {
62 self.hint = omaha_cohort.hint;
63 }
64 if omaha_cohort.name.is_some() {
65 self.name = omaha_cohort.name;
66 }
67 }
68
69 pub fn validate_name(name: &str) -> bool {
72 !name.is_empty()
73 && name.len() <= 1024
74 && name.chars().all(|c| ('\u{20}'..='\u{7e}').contains(&c))
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn test_cohort_new() {
84 let cohort = Cohort::new("my_cohort");
85 assert_eq!(Some("my_cohort".to_string()), cohort.id);
86 assert_eq!(None, cohort.hint);
87 assert_eq!(None, cohort.name);
88 }
89
90 #[test]
91 fn test_cohort_update_from_omaha() {
92 let mut cohort = Cohort::from_hint("hint");
93 let omaha_cohort = Cohort::new("my_cohort");
94 cohort.update_from_omaha(omaha_cohort);
95 assert_eq!(Some("my_cohort".to_string()), cohort.id);
96 assert_eq!(Some("hint".to_string()), cohort.hint);
97 assert_eq!(None, cohort.name);
98 }
99
100 #[test]
101 fn test_cohort_update_from_omaha_none() {
102 let mut cohort = Cohort {
103 id: Some("id".to_string()),
104 hint: Some("hint".to_string()),
105 name: Some("name".to_string()),
106 };
107 let expected_cohort = cohort.clone();
108 cohort.update_from_omaha(Cohort::default());
109 assert_eq!(cohort, expected_cohort);
110 }
111
112 #[test]
113 fn test_valid_cohort_names() {
114 assert!(Cohort::validate_name("some-channel"));
115 assert!(Cohort::validate_name("a"));
116
117 let max_len_name = "a".repeat(1024);
118 assert!(Cohort::validate_name(&max_len_name));
119 }
120
121 #[test]
122 fn test_invalid_cohort_name_length() {
123 assert!(!Cohort::validate_name(""));
124
125 let too_long_name = "a".repeat(1025);
126 assert!(!Cohort::validate_name(&too_long_name));
127 }
128
129 #[test]
130 fn test_invalid_cohort_name_chars() {
131 assert!(!Cohort::validate_name("some\u{09}channel"));
132 assert!(!Cohort::validate_name("some\u{07f}channel"));
133 assert!(!Cohort::validate_name("some\u{080}channel"));
134 }
135}