1use serde::Serialize;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
4#[serde(rename_all = "lowercase")]
5pub enum NavGroup {
6 Account,
7 Admin,
8}
9
10#[derive(Debug, Clone, Serialize)]
11pub struct NavItem {
12 pub href: String,
13 pub label: String,
14 pub group: NavGroup,
15 pub active: bool,
16}
17
18pub fn nav_items_for(is_admin: bool, current_path: &str) -> Vec<NavItem> {
19 let mut items = Vec::with_capacity(if is_admin { 5 } else { 2 });
23 if is_admin {
24 for (href, label) in [
25 ("/admin/applications", "Applications"),
26 ("/admin/sessions", "Sessions"),
27 ("/admin/audit", "Audit log"),
28 ] {
29 items.push(NavItem {
30 href: href.into(),
31 label: label.into(),
32 group: NavGroup::Admin,
33 active: current_path.starts_with(href),
34 });
35 }
36 }
37 for (href, label) in [("/settings", "Settings"), ("/logout", "Sign out")] {
38 items.push(NavItem {
39 href: href.into(),
40 label: label.into(),
41 group: NavGroup::Account,
42 active: current_path.starts_with(href),
43 });
44 }
45 items
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn user_gets_account_group_only_settings_active() {
54 let items = nav_items_for(false, "/settings");
55 assert_eq!(items.len(), 2);
56 assert!(items.iter().all(|i| i.group == NavGroup::Account));
57 assert!(items.iter().find(|i| i.href == "/settings").unwrap().active);
58 assert!(!items.iter().find(|i| i.href == "/logout").unwrap().active);
59 }
60
61 #[test]
62 fn admin_deep_path_highlights_applications() {
63 let items = nav_items_for(true, "/admin/applications/abc123");
64 assert_eq!(items.len(), 5);
65 let apps = items
66 .iter()
67 .find(|i| i.href == "/admin/applications")
68 .unwrap();
69 assert!(apps.active);
70 assert_eq!(apps.group, NavGroup::Admin);
71 }
72
73 #[test]
74 fn admin_nav_omits_users_until_route_lands() {
75 let items = nav_items_for(true, "/admin/applications");
76 assert!(items.iter().all(|i| i.href != "/admin/users"));
77 }
78
79 #[test]
80 fn logout_path_marks_sign_out_active() {
81 let items = nav_items_for(true, "/logout");
82 let signout = items.iter().find(|i| i.href == "/logout").unwrap();
83 assert!(signout.active);
84 assert_eq!(signout.group, NavGroup::Account);
85 }
86
87 #[test]
88 fn unrelated_path_has_no_active_item() {
89 let items = nav_items_for(true, "/totally-unrelated");
90 assert!(items.iter().all(|i| !i.active));
91 }
92}