role_system/
macros.rs

1//! Convenience macros for the role system.
2
3/// Macro for creating multiple permissions with a clean syntax.
4///
5/// # Examples
6///
7/// ```rust
8/// use role_system::permissions;
9///
10/// let perms = permissions![
11///     "read" => "documents",
12///     "write" => "documents",
13///     "delete" => "documents"
14/// ];
15/// ```
16#[macro_export]
17macro_rules! permissions {
18    ($($action:expr => $resource:expr),* $(,)?) => {
19        vec![$(
20            $crate::permission::Permission::new($action, $resource)
21        ),*]
22    };
23}
24
25/// Macro for creating a role with permissions in a single expression.
26///
27/// # Examples
28///
29/// ```rust
30/// use role_system::role_with_permissions;
31///
32/// let role = role_with_permissions! {
33///     name: "editor",
34///     description: "Content editor role",
35///     permissions: [
36///         "read" => "documents",
37///         "write" => "documents",
38///         "create" => "documents"
39///     ]
40/// };
41/// ```
42#[macro_export]
43macro_rules! role_with_permissions {
44    (
45        name: $name:expr,
46        description: $desc:expr,
47        permissions: [
48            $($action:expr => $resource:expr),* $(,)?
49        ]
50    ) => {
51        {
52            let mut role = $crate::role::Role::new($name).with_description($desc);
53            $(
54                role = role.add_permission($crate::permission::Permission::new($action, $resource));
55            )*
56            role
57        }
58    };
59    (
60        name: $name:expr,
61        permissions: [
62            $($action:expr => $resource:expr),* $(,)?
63        ]
64    ) => {
65        {
66            let mut role = $crate::role::Role::new($name);
67            $(
68                role = role.add_permission($crate::permission::Permission::new($action, $resource));
69            )*
70            role
71        }
72    };
73}
74
75/// Macro for creating subjects with different types.
76///
77/// # Examples
78///
79/// ```rust
80/// use role_system::subjects;
81///
82/// let (admin, service, device) = subjects! {
83///     user "admin" => display_name: "Administrator",
84///     service "api_service" => display_name: "API Service",
85///     device "printer_01" => display_name: "Office Printer"
86/// };
87/// ```
88#[macro_export]
89macro_rules! subjects {
90    (
91        $(
92            $type:ident $id:expr => display_name: $name:expr
93        ),* $(,)?
94    ) => {
95        ($(
96            $crate::subject::Subject::$type($id).with_display_name($name)
97        ),*)
98    };
99    (
100        $(
101            $type:ident $id:expr
102        ),* $(,)?
103    ) => {
104        ($(
105            $crate::subject::Subject::$type($id)
106        ),*)
107    };
108}
109
110/// Macro for defining conditional permissions with context requirements.
111///
112/// # Examples
113///
114/// ```rust
115/// use role_system::conditional_permission;
116///
117/// let perm = conditional_permission! {
118///     action: "access",
119///     resource: "secure_area",
120///     condition: |context| {
121///         context.get("clearance_level") == Some(&"top_secret".to_string()) &&
122///         context.get("time_of_day") == Some(&"business_hours".to_string())
123///     }
124/// };
125/// ```
126#[macro_export]
127macro_rules! conditional_permission {
128    (
129        action: $action:expr,
130        resource: $resource:expr,
131        condition: $condition:expr
132    ) => {
133        $crate::permission::Permission::with_condition($action, $resource, $condition)
134    };
135}
136
137/// Define a single role with its permissions using a fluent builder syntax.
138///
139/// # Example
140/// ```rust
141/// use role_system::define_role;
142///
143/// let admin_role = define_role!(admin {
144///     users: ["create", "read", "update", "delete"],
145///     roles: ["assign", "remove"],
146///     system: ["configure"]
147/// });
148/// ```
149#[macro_export]
150macro_rules! define_role {
151    (
152        $role_name:ident {
153            $(
154                $resource:ident: [$($action:literal),* $(,)?]
155            ),* $(,)?
156        }
157    ) => {
158        {
159            let mut builder = $crate::role::RoleBuilder::new().name(stringify!($role_name));
160
161            $(
162                let resource = stringify!($resource);
163                let actions = vec![$($action),*];
164                builder = builder.allow(resource, actions);
165            )*
166
167            builder.build().expect("Failed to build role")
168        }
169    };
170}
171
172/// Define multiple roles with their permissions declaratively.
173///
174/// # Example
175/// ```rust
176/// use role_system::define_roles;
177///
178/// let roles = define_roles! {
179///     admin {
180///         users: ["create", "read", "update", "delete"],
181///         roles: ["read", "assign", "remove"],
182///         system: ["configure", "monitor"]
183///     },
184///
185///     user {
186///         profile: ["read", "update"],
187///         posts: ["create", "read"]
188///     }
189/// };
190/// ```
191#[macro_export]
192macro_rules! define_roles {
193    (
194        $(
195            $role_name:ident {
196                $(
197                    $resource:ident: [$($action:literal),* $(,)?]
198                ),* $(,)?
199            }
200        ),* $(,)?
201    ) => {
202        {
203            use std::collections::HashMap;
204
205            let mut roles = HashMap::new();
206
207            $(
208                let mut builder = $crate::role::RoleBuilder::new().name(stringify!($role_name));
209
210                $(
211                    let resource = stringify!($resource);
212                    let actions = vec![$($action),*];
213                    builder = builder.allow(resource, actions);
214                )*
215
216                let role = builder.build().expect("Failed to build role");
217                roles.insert(stringify!($role_name).to_string(), role);
218            )*
219
220            roles
221        }
222    };
223}
224
225/// Quick macro for creating a permission with resource and actions.
226///
227/// # Example
228/// ```rust
229/// use role_system::permission;
230///
231/// let perm = permission!("users", "read");
232/// let perms = permission!("posts", ["create", "update", "delete"]);
233/// ```
234#[macro_export]
235macro_rules! permission {
236    ($resource:literal, $action:literal) => {
237        $crate::permission::Permission::new($action, $resource)
238    };
239
240    ($resource:literal, [$($action:literal),* $(,)?]) => {
241        vec![$(
242            $crate::permission::Permission::new($action, $resource)
243        ),*]
244    };
245}
246
247#[cfg(test)]
248mod tests {
249    use std::collections::HashMap;
250
251    #[test]
252    fn test_permissions_macro() {
253        let perms = permissions![
254            "read" => "documents",
255            "write" => "documents",
256            "delete" => "documents"
257        ];
258
259        assert_eq!(perms.len(), 3);
260        assert_eq!(perms[0].action(), "read");
261        assert_eq!(perms[0].resource_type(), "documents");
262    }
263
264    #[test]
265    fn test_role_with_permissions_macro() {
266        let role = role_with_permissions! {
267            name: "editor",
268            description: "Content editor role",
269            permissions: [
270                "read" => "documents",
271                "write" => "documents"
272            ]
273        };
274
275        assert_eq!(role.name(), "editor");
276        assert_eq!(role.description(), Some("Content editor role"));
277        assert_eq!(role.permissions().permissions().len(), 2);
278    }
279
280    #[test]
281    fn test_subjects_macro() {
282        let (admin, service) = subjects! {
283            user "admin_user" => display_name: "Administrator",
284            service "api_service" => display_name: "API Service"
285        };
286
287        assert_eq!(admin.id(), "admin_user");
288        assert_eq!(admin.display_name(), Some("Administrator"));
289        assert_eq!(service.id(), "api_service");
290        assert_eq!(service.display_name(), Some("API Service"));
291    }
292
293    #[test]
294    fn test_conditional_permission_macro() {
295        let perm = conditional_permission! {
296            action: "access",
297            resource: "secure_area",
298            condition: |context: &HashMap<String, String>| {
299                context.get("clearance") == Some(&"top_secret".to_string())
300            }
301        };
302
303        assert_eq!(perm.action(), "access");
304        assert_eq!(perm.resource_type(), "secure_area");
305
306        let mut valid_context = HashMap::new();
307        valid_context.insert("clearance".to_string(), "top_secret".to_string());
308        assert!(perm.is_granted("access", "secure_area", &valid_context));
309
310        let invalid_context = HashMap::new();
311        assert!(!perm.is_granted("access", "secure_area", &invalid_context));
312    }
313
314    #[test]
315    fn test_define_role_macro() {
316        let role = define_role!(admin {
317            users: ["create", "read", "update", "delete"],
318            roles: ["assign", "remove"]
319        });
320
321        assert_eq!(role.name(), "admin");
322        assert_eq!(role.permissions().len(), 6); // 4 user actions + 2 role actions
323    }
324
325    #[test]
326    fn test_permission_macro() {
327        let single_perm = permission!("users", "read");
328        assert_eq!(single_perm.action(), "read");
329        assert_eq!(single_perm.resource_type(), "users");
330
331        let multi_perms = permission!("posts", ["create", "update", "delete"]);
332        assert_eq!(multi_perms.len(), 3);
333    }
334
335    #[test]
336    fn test_define_roles_macro() {
337        let roles = define_roles! {
338            admin {
339                users: ["create", "read", "update", "delete"],
340                roles: ["assign"]
341            },
342
343            user {
344                profile: ["read", "update"],
345                posts: ["create"]
346            }
347        };
348
349        assert_eq!(roles.len(), 2);
350        assert!(roles.contains_key("admin"));
351        assert!(roles.contains_key("user"));
352
353        let admin_role = &roles["admin"];
354        assert_eq!(admin_role.name(), "admin");
355        assert_eq!(admin_role.permissions().len(), 5); // 4 user actions + 1 role action
356    }
357}