Skip to main content

allowthem_server/
nav.rs

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 { 6 } else { 2 });
20    if is_admin {
21        for (href, label) in [
22            ("/admin/applications", "Applications"),
23            ("/admin/users", "Users"),
24            ("/admin/sessions", "Sessions"),
25            ("/admin/audit", "Audit log"),
26        ] {
27            items.push(NavItem {
28                href: href.into(),
29                label: label.into(),
30                group: NavGroup::Admin,
31                active: current_path.starts_with(href),
32            });
33        }
34    }
35    for (href, label) in [("/settings", "Settings"), ("/logout", "Sign out")] {
36        items.push(NavItem {
37            href: href.into(),
38            label: label.into(),
39            group: NavGroup::Account,
40            active: current_path.starts_with(href),
41        });
42    }
43    items
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn user_gets_account_group_only_settings_active() {
52        let items = nav_items_for(false, "/settings");
53        assert_eq!(items.len(), 2);
54        assert!(items.iter().all(|i| i.group == NavGroup::Account));
55        assert!(items.iter().find(|i| i.href == "/settings").unwrap().active);
56        assert!(!items.iter().find(|i| i.href == "/logout").unwrap().active);
57    }
58
59    #[test]
60    fn admin_deep_path_highlights_applications() {
61        let items = nav_items_for(true, "/admin/applications/abc123");
62        assert_eq!(items.len(), 6);
63        let apps = items
64            .iter()
65            .find(|i| i.href == "/admin/applications")
66            .unwrap();
67        assert!(apps.active);
68        assert_eq!(apps.group, NavGroup::Admin);
69    }
70
71    #[test]
72    fn admin_nav_includes_users_after_route_lands() {
73        let items = nav_items_for(true, "/admin/applications");
74        assert!(items.iter().any(|i| i.href == "/admin/users"));
75    }
76
77    #[test]
78    fn logout_path_marks_sign_out_active() {
79        let items = nav_items_for(true, "/logout");
80        let signout = items.iter().find(|i| i.href == "/logout").unwrap();
81        assert!(signout.active);
82        assert_eq!(signout.group, NavGroup::Account);
83    }
84
85    #[test]
86    fn unrelated_path_has_no_active_item() {
87        let items = nav_items_for(true, "/totally-unrelated");
88        assert!(items.iter().all(|i| !i.active));
89    }
90}