Skip to main content

cloudillo_core/
roles.rs

1// SPDX-FileCopyrightText: Szilárd Hajba
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4//! Role hierarchy and expansion logic
5//!
6//! This module defines the built-in role hierarchy and provides utilities
7//! for expanding hierarchical roles.
8
9/// Role hierarchy for profile-level permissions
10/// Higher roles inherit all permissions from lower roles
11pub const ROLE_HIERARCHY: &[&str] =
12	&["public", "follower", "supporter", "contributor", "moderator", "leader"];
13
14/// Expands hierarchical roles from highest role to all inherited roles
15///
16/// Given a list of roles (typically just the highest one), this function
17/// returns a comma-separated string of all roles from "public" up to and
18/// including the highest role in the hierarchy.
19///
20/// # Examples
21/// ```
22/// use cloudillo_core::roles::expand_roles;
23/// assert_eq!(expand_roles(&["moderator".into()]), "public,follower,supporter,contributor,moderator");
24/// assert_eq!(expand_roles(&["contributor".into(), "moderator".into()]), "public,follower,supporter,contributor,moderator");
25/// assert_eq!(expand_roles(&[]), "");
26/// ```
27pub fn expand_roles(highest_roles: &[Box<str>]) -> String {
28	if highest_roles.is_empty() {
29		return String::new();
30	}
31
32	let mut highest_idx: Option<usize> = None;
33	for role in highest_roles {
34		if let Some(idx) = ROLE_HIERARCHY.iter().position(|&r| r == role.as_ref()) {
35			highest_idx = Some(highest_idx.map_or(idx, |h| h.max(idx)));
36		}
37	}
38
39	// Return comma-separated list of all roles up to highest, or empty if no valid roles found
40	match highest_idx {
41		Some(idx) => ROLE_HIERARCHY[..=idx].join(","),
42		None => String::new(),
43	}
44}
45
46#[cfg(test)]
47mod tests {
48	use super::*;
49
50	#[test]
51	fn test_expand_roles_empty() {
52		assert_eq!(expand_roles(&[]), "");
53	}
54
55	#[test]
56	fn test_expand_roles_single() {
57		assert_eq!(expand_roles(&["public".into()]), "public");
58		assert_eq!(expand_roles(&["follower".into()]), "public,follower");
59		assert_eq!(
60			expand_roles(&["moderator".into()]),
61			"public,follower,supporter,contributor,moderator"
62		);
63		assert_eq!(
64			expand_roles(&["leader".into()]),
65			"public,follower,supporter,contributor,moderator,leader"
66		);
67	}
68
69	#[test]
70	fn test_expand_roles_multiple() {
71		// Takes highest role
72		assert_eq!(
73			expand_roles(&["contributor".into(), "moderator".into()]),
74			"public,follower,supporter,contributor,moderator"
75		);
76		assert_eq!(
77			expand_roles(&["public".into(), "leader".into()]),
78			"public,follower,supporter,contributor,moderator,leader"
79		);
80	}
81
82	#[test]
83	fn test_expand_roles_unknown() {
84		// Unknown roles are ignored
85		assert_eq!(expand_roles(&["unknown".into()]), "");
86		assert_eq!(
87			expand_roles(&["unknown".into(), "contributor".into()]),
88			"public,follower,supporter,contributor"
89		);
90	}
91}
92
93// vim: ts=4