1use serde::{Deserialize, Serialize};
7use std::collections::BTreeSet;
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
23pub struct Role {
24 name: String,
26 index: Option<usize>,
28}
29
30impl Role {
31 #[must_use]
33 pub fn new(name: impl Into<String>) -> Self {
34 Self {
35 name: name.into(),
36 index: None,
37 }
38 }
39
40 #[must_use]
42 pub fn indexed(name: impl Into<String>, index: usize) -> Self {
43 Self {
44 name: name.into(),
45 index: Some(index),
46 }
47 }
48
49 #[must_use]
51 pub fn name(&self) -> &str {
52 &self.name
53 }
54
55 #[must_use]
57 pub fn index(&self) -> Option<usize> {
58 self.index
59 }
60
61 #[must_use]
63 pub fn full_name(&self) -> String {
64 match self.index {
65 Some(i) => format!("{}_{}", self.name, i),
66 None => self.name.clone(),
67 }
68 }
69
70 #[must_use]
72 pub fn matches_name(&self, other: &Role) -> bool {
73 self.name == other.name
74 }
75}
76
77impl std::fmt::Display for Role {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 write!(f, "{}", self.full_name())
80 }
81}
82
83impl From<&str> for Role {
84 fn from(name: &str) -> Self {
85 Role::new(name)
86 }
87}
88
89impl From<String> for Role {
90 fn from(name: String) -> Self {
91 Role::new(name)
92 }
93}
94
95#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
99pub struct RoleSet {
100 roles: BTreeSet<Role>,
101}
102
103impl RoleSet {
104 #[must_use]
106 pub fn new() -> Self {
107 Self {
108 roles: BTreeSet::new(),
109 }
110 }
111
112 #[must_use]
116 pub fn from_roles(iter: impl IntoIterator<Item = Role>) -> Self {
117 Self {
118 roles: iter.into_iter().collect(),
119 }
120 }
121
122 pub fn insert(&mut self, role: Role) -> bool {
124 self.roles.insert(role)
125 }
126
127 pub fn remove(&mut self, role: &Role) -> bool {
129 self.roles.remove(role)
130 }
131
132 #[must_use]
134 pub fn contains(&self, role: &Role) -> bool {
135 self.roles.contains(role)
136 }
137
138 #[must_use]
140 pub fn contains_name(&self, name: &str) -> bool {
141 self.roles.iter().any(|r| r.name == name)
142 }
143
144 #[must_use]
146 pub fn len(&self) -> usize {
147 self.roles.len()
148 }
149
150 #[must_use]
152 pub fn is_empty(&self) -> bool {
153 self.roles.is_empty()
154 }
155
156 pub fn iter(&self) -> impl Iterator<Item = &Role> {
158 self.roles.iter()
159 }
160
161 #[must_use]
163 pub fn names(&self) -> Vec<String> {
164 self.roles.iter().map(|r| r.full_name()).collect()
165 }
166}
167
168impl IntoIterator for RoleSet {
169 type Item = Role;
170 type IntoIter = std::collections::btree_set::IntoIter<Role>;
171
172 fn into_iter(self) -> Self::IntoIter {
173 self.roles.into_iter()
174 }
175}
176
177impl<'a> IntoIterator for &'a RoleSet {
178 type Item = &'a Role;
179 type IntoIter = std::collections::btree_set::Iter<'a, Role>;
180
181 fn into_iter(self) -> Self::IntoIter {
182 self.roles.iter()
183 }
184}
185
186impl FromIterator<Role> for RoleSet {
187 fn from_iter<T: IntoIterator<Item = Role>>(iter: T) -> Self {
188 Self {
189 roles: iter.into_iter().collect(),
190 }
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn test_role_new() {
200 let role = Role::new("Client");
201 assert_eq!(role.name(), "Client");
202 assert_eq!(role.index(), None);
203 assert_eq!(role.full_name(), "Client");
204 }
205
206 #[test]
207 fn test_role_indexed() {
208 let role = Role::indexed("Worker", 2);
209 assert_eq!(role.name(), "Worker");
210 assert_eq!(role.index(), Some(2));
211 assert_eq!(role.full_name(), "Worker_2");
212 }
213
214 #[test]
215 fn test_role_display() {
216 let simple = Role::new("Server");
217 let indexed = Role::indexed("Node", 5);
218
219 assert_eq!(format!("{}", simple), "Server");
220 assert_eq!(format!("{}", indexed), "Node_5");
221 }
222
223 #[test]
224 fn test_role_set() {
225 let mut set = RoleSet::new();
226 set.insert(Role::new("A"));
227 set.insert(Role::new("B"));
228 set.insert(Role::new("C"));
229
230 assert_eq!(set.len(), 3);
231 assert!(set.contains_name("A"));
232 assert!(set.contains_name("B"));
233 assert!(!set.contains_name("D"));
234 }
235
236 #[test]
237 fn test_role_set_from_iter() {
238 let roles = vec![Role::new("X"), Role::new("Y"), Role::new("Z")];
239 let set: RoleSet = roles.into_iter().collect();
240
241 assert_eq!(set.len(), 3);
242 assert!(set.contains_name("X"));
243 }
244
245 #[test]
246 fn test_role_set_names_are_sorted() {
247 let mut set = RoleSet::new();
248 set.insert(Role::new("Worker"));
249 set.insert(Role::new("Alpha"));
250 set.insert(Role::indexed("Node", 2));
251 set.insert(Role::indexed("Node", 1));
252
253 assert_eq!(
254 set.names(),
255 vec![
256 "Alpha".to_string(),
257 "Node_1".to_string(),
258 "Node_2".to_string(),
259 "Worker".to_string()
260 ]
261 );
262 }
263}