use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Role {
name: String,
index: Option<usize>,
}
impl Role {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
index: None,
}
}
#[must_use]
pub fn indexed(name: impl Into<String>, index: usize) -> Self {
Self {
name: name.into(),
index: Some(index),
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn index(&self) -> Option<usize> {
self.index
}
#[must_use]
pub fn full_name(&self) -> String {
match self.index {
Some(i) => format!("{}_{}", self.name, i),
None => self.name.clone(),
}
}
#[must_use]
pub fn matches_name(&self, other: &Role) -> bool {
self.name == other.name
}
}
impl std::fmt::Display for Role {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.full_name())
}
}
impl From<&str> for Role {
fn from(name: &str) -> Self {
Role::new(name)
}
}
impl From<String> for Role {
fn from(name: String) -> Self {
Role::new(name)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct RoleSet {
roles: BTreeSet<Role>,
}
impl RoleSet {
#[must_use]
pub fn new() -> Self {
Self {
roles: BTreeSet::new(),
}
}
#[must_use]
pub fn from_roles(iter: impl IntoIterator<Item = Role>) -> Self {
Self {
roles: iter.into_iter().collect(),
}
}
pub fn insert(&mut self, role: Role) -> bool {
self.roles.insert(role)
}
pub fn remove(&mut self, role: &Role) -> bool {
self.roles.remove(role)
}
#[must_use]
pub fn contains(&self, role: &Role) -> bool {
self.roles.contains(role)
}
#[must_use]
pub fn contains_name(&self, name: &str) -> bool {
self.roles.iter().any(|r| r.name == name)
}
#[must_use]
pub fn len(&self) -> usize {
self.roles.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.roles.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &Role> {
self.roles.iter()
}
#[must_use]
pub fn names(&self) -> Vec<String> {
self.roles.iter().map(|r| r.full_name()).collect()
}
}
impl IntoIterator for RoleSet {
type Item = Role;
type IntoIter = std::collections::btree_set::IntoIter<Role>;
fn into_iter(self) -> Self::IntoIter {
self.roles.into_iter()
}
}
impl<'a> IntoIterator for &'a RoleSet {
type Item = &'a Role;
type IntoIter = std::collections::btree_set::Iter<'a, Role>;
fn into_iter(self) -> Self::IntoIter {
self.roles.iter()
}
}
impl FromIterator<Role> for RoleSet {
fn from_iter<T: IntoIterator<Item = Role>>(iter: T) -> Self {
Self {
roles: iter.into_iter().collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_role_new() {
let role = Role::new("Client");
assert_eq!(role.name(), "Client");
assert_eq!(role.index(), None);
assert_eq!(role.full_name(), "Client");
}
#[test]
fn test_role_indexed() {
let role = Role::indexed("Worker", 2);
assert_eq!(role.name(), "Worker");
assert_eq!(role.index(), Some(2));
assert_eq!(role.full_name(), "Worker_2");
}
#[test]
fn test_role_display() {
let simple = Role::new("Server");
let indexed = Role::indexed("Node", 5);
assert_eq!(format!("{}", simple), "Server");
assert_eq!(format!("{}", indexed), "Node_5");
}
#[test]
fn test_role_set() {
let mut set = RoleSet::new();
set.insert(Role::new("A"));
set.insert(Role::new("B"));
set.insert(Role::new("C"));
assert_eq!(set.len(), 3);
assert!(set.contains_name("A"));
assert!(set.contains_name("B"));
assert!(!set.contains_name("D"));
}
#[test]
fn test_role_set_from_iter() {
let roles = vec![Role::new("X"), Role::new("Y"), Role::new("Z")];
let set: RoleSet = roles.into_iter().collect();
assert_eq!(set.len(), 3);
assert!(set.contains_name("X"));
}
#[test]
fn test_role_set_names_are_sorted() {
let mut set = RoleSet::new();
set.insert(Role::new("Worker"));
set.insert(Role::new("Alpha"));
set.insert(Role::indexed("Node", 2));
set.insert(Role::indexed("Node", 1));
assert_eq!(
set.names(),
vec![
"Alpha".to_string(),
"Node_1".to_string(),
"Node_2".to_string(),
"Worker".to_string()
]
);
}
}