role_system/
hierarchy.rs

1//! Role hierarchy types and tree structures for optional hierarchy access.
2//!
3//! This module provides types for representing role hierarchies in structured
4//! formats, enabling use cases like API responses, admin interfaces, JWT claims,
5//! and database integration while maintaining backward compatibility.
6use crate::role::Role;
7#[cfg(feature = "persistence")]
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11/// Represents a complete role hierarchy tree structure.
12///
13/// This type provides a structured view of role relationships that can be
14/// used for visualization, API responses, or external system integration.
15///
16/// # Example
17/// ```no_run
18/// # use role_system::{AsyncRoleSystem, RoleSystem, RoleSystemConfig, MemoryStorage};
19/// # use role_system::hierarchy::RoleHierarchyTree;
20/// # tokio_test::block_on(async {
21/// let storage = MemoryStorage::new();
22/// let role_sys = RoleSystem::with_storage(storage, RoleSystemConfig::default());
23/// let role_system = AsyncRoleSystem::new(role_sys);
24///
25/// // Get hierarchy tree from role system
26/// let tree = role_system.get_hierarchy_tree(None).await?;
27/// println!("Total roles: {}, Max depth: {}", tree.total_roles, tree.max_depth);
28/// # Ok::<(), role_system::Error>(())
29/// # });
30/// ```
31#[derive(Debug, Clone)]
32#[cfg_attr(feature = "persistence", derive(Serialize, Deserialize))]
33pub struct RoleHierarchyTree {
34    /// Root node of the hierarchy tree
35    pub root: RoleNode,
36    /// Total number of roles in the tree
37    pub total_roles: usize,
38    /// Maximum depth of the hierarchy
39    pub max_depth: usize,
40    /// Metadata about the tree structure
41    pub metadata: HierarchyMetadata,
42}
43
44/// Represents a single node in the role hierarchy tree.
45///
46/// Each node contains a role and its direct children, along with
47/// structural information like depth in the hierarchy.
48#[derive(Debug, Clone)]
49#[cfg_attr(feature = "persistence", derive(Serialize, Deserialize))]
50pub struct RoleNode {
51    /// The role at this node
52    pub role: Role,
53    /// Direct child roles
54    pub children: Vec<RoleNode>,
55    /// Depth in the hierarchy (root = 0)
56    pub depth: usize,
57    /// Number of descendants (including indirect children)
58    pub descendant_count: usize,
59}
60
61/// Metadata about a role hierarchy structure.
62#[derive(Debug, Clone)]
63#[cfg_attr(feature = "persistence", derive(Serialize, Deserialize))]
64pub struct HierarchyMetadata {
65    /// When the hierarchy was generated
66    #[cfg(feature = "persistence")]
67    pub generated_at: chrono::DateTime<chrono::Utc>,
68    /// Version of the hierarchy schema
69    pub schema_version: String,
70    /// Total permission count across all roles
71    pub total_permissions: usize,
72    /// Performance metrics for generation
73    pub generation_time_ms: u64,
74}
75
76/// Represents a relationship between two roles in the hierarchy.
77///
78/// This type captures both direct parent-child relationships and
79/// inherited relationships through the hierarchy chain.
80#[derive(Debug, Clone)]
81#[cfg_attr(feature = "persistence", derive(Serialize, Deserialize))]
82pub struct RoleRelationship {
83    /// ID of the child role
84    pub child_role_id: String,
85    /// ID of the parent role
86    pub parent_role_id: String,
87    /// Type of relationship (direct or inherited)
88    pub relationship_type: RelationshipType,
89    /// When this relationship was created (if tracked)
90    #[cfg(feature = "persistence")]
91    pub created_at: Option<chrono::DateTime<chrono::Utc>>,
92    /// Additional metadata about the relationship
93    pub metadata: HashMap<String, String>,
94}
95
96/// Type of relationship between roles in the hierarchy.
97#[derive(Debug, Clone, PartialEq, Eq)]
98#[cfg_attr(feature = "persistence", derive(Serialize, Deserialize))]
99pub enum RelationshipType {
100    /// Direct parent-child relationship
101    Direct,
102    /// Inherited relationship through hierarchy chain
103    Inherited,
104}
105
106/// Configuration for hierarchy access and traversal.
107///
108/// This configuration controls whether hierarchy information can be
109/// accessed and how traversal operations behave.
110#[derive(Debug, Clone)]
111pub struct HierarchyConfig {
112    /// Enable hierarchy access methods (default: false for backward compatibility)
113    pub enable_hierarchy_access: bool,
114    /// Maximum hierarchy depth to prevent infinite recursion
115    pub max_hierarchy_depth: usize,
116    /// Cache hierarchy calculations for performance
117    pub cache_hierarchy: bool,
118    /// Maximum number of roles to traverse in a single operation
119    pub max_traversal_size: usize,
120    /// Include permission counts in hierarchy metadata
121    pub include_permission_counts: bool,
122}
123
124impl Default for HierarchyConfig {
125    fn default() -> Self {
126        Self {
127            enable_hierarchy_access: false, // Maintain current behavior by default
128            max_hierarchy_depth: 10,
129            cache_hierarchy: true,
130            max_traversal_size: 1000,
131            include_permission_counts: true,
132        }
133    }
134}
135
136/// Builder for creating hierarchy configurations.
137///
138/// # Example
139/// ```rust
140/// use role_system::hierarchy::HierarchyConfigBuilder;
141///
142/// let config = HierarchyConfigBuilder::new()
143///     .enable_hierarchy_access(true)
144///     .max_depth(15)
145///     .enable_caching(true)
146///     .build();
147/// ```
148pub struct HierarchyConfigBuilder {
149    config: HierarchyConfig,
150}
151
152impl HierarchyConfigBuilder {
153    /// Create a new hierarchy configuration builder.
154    pub fn new() -> Self {
155        Self {
156            config: HierarchyConfig::default(),
157        }
158    }
159
160    /// Enable or disable hierarchy access methods.
161    pub fn enable_hierarchy_access(mut self, enable: bool) -> Self {
162        self.config.enable_hierarchy_access = enable;
163        self
164    }
165
166    /// Set the maximum hierarchy depth.
167    pub fn max_depth(mut self, depth: usize) -> Self {
168        self.config.max_hierarchy_depth = depth;
169        self
170    }
171
172    /// Enable or disable hierarchy caching.
173    pub fn enable_caching(mut self, enable: bool) -> Self {
174        self.config.cache_hierarchy = enable;
175        self
176    }
177
178    /// Set the maximum traversal size.
179    pub fn max_traversal_size(mut self, size: usize) -> Self {
180        self.config.max_traversal_size = size;
181        self
182    }
183
184    /// Include permission counts in metadata.
185    pub fn include_permission_counts(mut self, include: bool) -> Self {
186        self.config.include_permission_counts = include;
187        self
188    }
189
190    /// Build the hierarchy configuration.
191    pub fn build(self) -> HierarchyConfig {
192        self.config
193    }
194}
195
196impl Default for HierarchyConfigBuilder {
197    fn default() -> Self {
198        Self::new()
199    }
200}
201
202impl RoleHierarchyTree {
203    /// Create a new empty hierarchy tree.
204    pub fn new(root: RoleNode) -> Self {
205        let total_roles = root.descendant_count + 1;
206        let max_depth = Self::calculate_max_depth(&root);
207
208        Self {
209            root,
210            total_roles,
211            max_depth,
212            metadata: HierarchyMetadata {
213                #[cfg(feature = "persistence")]
214                generated_at: chrono::Utc::now(),
215                schema_version: "1.1.0".to_string(),
216                total_permissions: 0, // Will be calculated later
217                generation_time_ms: 0,
218            },
219        }
220    }
221
222    /// Calculate the maximum depth of the hierarchy tree.
223    fn calculate_max_depth(node: &RoleNode) -> usize {
224        if node.children.is_empty() {
225            node.depth
226        } else {
227            node.children
228                .iter()
229                .map(Self::calculate_max_depth)
230                .max()
231                .unwrap_or(node.depth)
232        }
233    }
234
235    /// Get all roles in the tree as a flattened list.
236    pub fn flatten(&self) -> Vec<&Role> {
237        let mut roles = Vec::new();
238        Self::flatten_node(&self.root, &mut roles);
239        roles
240    }
241
242    /// Recursively flatten a node and its children.
243    fn flatten_node<'a>(node: &'a RoleNode, roles: &mut Vec<&'a Role>) {
244        roles.push(&node.role);
245        for child in &node.children {
246            Self::flatten_node(child, roles);
247        }
248    }
249
250    /// Find a node by role ID.
251    pub fn find_node(&self, role_id: &str) -> Option<&RoleNode> {
252        Self::find_node_recursive(&self.root, role_id)
253    }
254
255    /// Recursively search for a node by role ID.
256    fn find_node_recursive<'a>(node: &'a RoleNode, role_id: &str) -> Option<&'a RoleNode> {
257        if node.role.id() == role_id {
258            return Some(node);
259        }
260
261        for child in &node.children {
262            if let Some(found) = Self::find_node_recursive(child, role_id) {
263                return Some(found);
264            }
265        }
266
267        None
268    }
269}
270
271impl RoleNode {
272    /// Create a new role node.
273    pub fn new(role: Role, depth: usize) -> Self {
274        Self {
275            role,
276            children: Vec::new(),
277            depth,
278            descendant_count: 0,
279        }
280    }
281
282    /// Add a child node.
283    pub fn add_child(&mut self, child: RoleNode) {
284        self.descendant_count += child.descendant_count + 1;
285        self.children.push(child);
286    }
287
288    /// Get all descendant role IDs.
289    pub fn get_descendant_ids(&self) -> Vec<String> {
290        let mut ids = Vec::new();
291        for child in &self.children {
292            ids.push(child.role.id().to_string());
293            ids.extend(child.get_descendant_ids());
294        }
295        ids
296    }
297
298    /// Check if this node is a leaf (has no children).
299    pub fn is_leaf(&self) -> bool {
300        self.children.is_empty()
301    }
302
303    /// Check if this node is the root (depth 0).
304    pub fn is_root(&self) -> bool {
305        self.depth == 0
306    }
307}
308
309impl RoleRelationship {
310    /// Create a new role relationship.
311    pub fn new(
312        child_role_id: String,
313        parent_role_id: String,
314        relationship_type: RelationshipType,
315    ) -> Self {
316        Self {
317            child_role_id,
318            parent_role_id,
319            relationship_type,
320            #[cfg(feature = "persistence")]
321            created_at: Some(chrono::Utc::now()),
322            metadata: HashMap::new(),
323        }
324    }
325
326    /// Create a direct relationship.
327    pub fn direct(child_role_id: String, parent_role_id: String) -> Self {
328        Self::new(child_role_id, parent_role_id, RelationshipType::Direct)
329    }
330
331    /// Create an inherited relationship.
332    pub fn inherited(child_role_id: String, parent_role_id: String) -> Self {
333        Self::new(child_role_id, parent_role_id, RelationshipType::Inherited)
334    }
335
336    /// Add metadata to the relationship.
337    pub fn with_metadata(mut self, key: String, value: String) -> Self {
338        self.metadata.insert(key, value);
339        self
340    }
341
342    /// Check if this is a direct relationship.
343    pub fn is_direct(&self) -> bool {
344        self.relationship_type == RelationshipType::Direct
345    }
346
347    /// Check if this is an inherited relationship.
348    pub fn is_inherited(&self) -> bool {
349        self.relationship_type == RelationshipType::Inherited
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356    use crate::role::Role;
357
358    #[test]
359    fn test_hierarchy_config_builder() {
360        let config = HierarchyConfigBuilder::new()
361            .enable_hierarchy_access(true)
362            .max_depth(15)
363            .enable_caching(false)
364            .max_traversal_size(500)
365            .build();
366
367        assert!(config.enable_hierarchy_access);
368        assert_eq!(config.max_hierarchy_depth, 15);
369        assert!(!config.cache_hierarchy);
370        assert_eq!(config.max_traversal_size, 500);
371    }
372
373    #[test]
374    fn test_role_node_creation() {
375        let role = Role::new("test_role");
376        let node = RoleNode::new(role, 2);
377
378        assert_eq!(node.depth, 2);
379        assert_eq!(node.descendant_count, 0);
380        assert!(node.is_leaf());
381        assert!(!node.is_root());
382    }
383
384    #[test]
385    fn test_role_relationship_creation() {
386        let rel = RoleRelationship::direct("child".to_string(), "parent".to_string());
387
388        assert_eq!(rel.child_role_id, "child");
389        assert_eq!(rel.parent_role_id, "parent");
390        assert!(rel.is_direct());
391        assert!(!rel.is_inherited());
392    }
393
394    #[test]
395    fn test_hierarchy_tree_creation() {
396        let role = Role::new("root");
397        let root_node = RoleNode::new(role, 0);
398        let tree = RoleHierarchyTree::new(root_node);
399
400        assert_eq!(tree.total_roles, 1);
401        assert_eq!(tree.max_depth, 0);
402        assert_eq!(tree.metadata.schema_version, "1.1.0");
403    }
404}