Skip to main content

telltale_types/
role.rs

1//! Roles in Multiparty Session Types
2//!
3//! Roles represent participants in a multiparty session.
4//! Each role has a unique name and optionally an index for parameterized protocols.
5
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeSet;
8
9/// A role (participant) in a multiparty session.
10///
11/// # Examples
12///
13/// ```
14/// use telltale_types::Role;
15///
16/// let client = Role::new("Client");
17/// let server = Role::indexed("Worker", 0);
18///
19/// assert_eq!(client.name(), "Client");
20/// assert_eq!(server.full_name(), "Worker_0");
21/// ```
22#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
23pub struct Role {
24    /// The base name of the role
25    name: String,
26    /// Optional index for parameterized roles
27    index: Option<usize>,
28}
29
30impl Role {
31    /// Create a new role with the given name
32    #[must_use]
33    pub fn new(name: impl Into<String>) -> Self {
34        Self {
35            name: name.into(),
36            index: None,
37        }
38    }
39
40    /// Create an indexed role (for parameterized protocols)
41    #[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    /// Get the base name of the role
50    #[must_use]
51    pub fn name(&self) -> &str {
52        &self.name
53    }
54
55    /// Get the index if this is a parameterized role
56    #[must_use]
57    pub fn index(&self) -> Option<usize> {
58        self.index
59    }
60
61    /// Get the full name including index
62    #[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    /// Check if this role matches another by name (ignoring index)
71    #[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/// A set of roles participating in a protocol.
96///
97/// Provides convenient operations for working with collections of roles.
98#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
99pub struct RoleSet {
100    roles: BTreeSet<Role>,
101}
102
103impl RoleSet {
104    /// Create an empty role set
105    #[must_use]
106    pub fn new() -> Self {
107        Self {
108            roles: BTreeSet::new(),
109        }
110    }
111
112    /// Create a role set from an iterator
113    ///
114    /// Note: Consider using `collect()` directly via the `FromIterator` implementation.
115    #[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    /// Add a role to the set
123    pub fn insert(&mut self, role: Role) -> bool {
124        self.roles.insert(role)
125    }
126
127    /// Remove a role from the set
128    pub fn remove(&mut self, role: &Role) -> bool {
129        self.roles.remove(role)
130    }
131
132    /// Check if the set contains a role
133    #[must_use]
134    pub fn contains(&self, role: &Role) -> bool {
135        self.roles.contains(role)
136    }
137
138    /// Check if the set contains a role by name
139    #[must_use]
140    pub fn contains_name(&self, name: &str) -> bool {
141        self.roles.iter().any(|r| r.name == name)
142    }
143
144    /// Get the number of roles
145    #[must_use]
146    pub fn len(&self) -> usize {
147        self.roles.len()
148    }
149
150    /// Check if the set is empty
151    #[must_use]
152    pub fn is_empty(&self) -> bool {
153        self.roles.is_empty()
154    }
155
156    /// Iterate over the roles
157    pub fn iter(&self) -> impl Iterator<Item = &Role> {
158        self.roles.iter()
159    }
160
161    /// Get roles as a vector of names
162    #[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}