Skip to main content

tablo_test/protocol/
sidebar.rs

1//! The shell's navigation: the sidebar's entries and groups, the badge an entry shows, an area's
2//! link back to the panel, and the topbar's trail.
3
4use super::dom::{Attrs, attr_value, open_tags, strip_text, tags_where};
5
6/// The badge text of the sidebar entry linking to `href`, or `None` when that entry shows no
7/// badge or no entry links there.
8pub fn nav_badge(html: &str, href: &str) -> Option<String> {
9    let menu = |name: &'static str| {
10        move |_: &str, attrs: &Attrs| attr_value(attrs, "data-sidebar").as_deref() == Some(name)
11    };
12    let (_, _, entry) = tags_where(html, menu("menu-item"))
13        .into_iter()
14        .find(|(_, _, inner)| {
15            open_tags(inner).iter().any(|(tag, attrs, _)| {
16                tag == "a" && attr_value(attrs, "href").as_deref() == Some(href)
17            })
18        })?;
19    let (_, _, badge) = tags_where(&entry, menu("menu-badge")).into_iter().next()?;
20    Some(strip_text(&badge))
21}
22
23/// The `href` of each sidebar entry that links somewhere, in the order the sidebar lists them.
24pub fn nav_links(html: &str) -> Vec<String> {
25    open_tags(html)
26        .into_iter()
27        .filter(|(tag, attrs, _)| {
28            tag == "a" && attr_value(attrs, "data-sidebar").as_deref() == Some("menu-button")
29        })
30        .filter_map(|(_, attrs, _)| attr_value(&attrs, "href"))
31        .collect()
32}
33
34/// One labelled group of the sidebar: its label and the `href` of each entry it lists.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct NavGroup {
37    pub label: String,
38    pub links: Vec<String>,
39}
40
41/// The sidebar's groups, in the order it lists them.
42pub fn nav_groups(html: &str) -> Vec<NavGroup> {
43    tags_where(html, |_, attrs| {
44        attr_value(attrs, "data-sidebar").as_deref() == Some("group")
45    })
46    .into_iter()
47    .filter_map(|(_, _, group)| {
48        let (_, _, label) = tags_where(&group, |_, attrs| {
49            attr_value(attrs, "data-sidebar").as_deref() == Some("group-label")
50        })
51        .into_iter()
52        .next()?;
53        Some(NavGroup {
54            label: strip_text(&label),
55            links: nav_links(&group),
56        })
57    })
58    .collect()
59}
60
61/// The `href` of an area's link back to the panel, or `None` outside every area.
62pub fn area_back_link(html: &str) -> Option<String> {
63    open_tags(html)
64        .into_iter()
65        .find(|(tag, attrs, _)| tag == "a" && attr_value(attrs, "data-area-back").is_some())
66        .and_then(|(_, attrs, _)| attr_value(&attrs, "href"))
67}
68
69/// The topbar's trail: each step's text, with its `href` unless it is the page itself.
70pub fn trail(html: &str) -> Vec<(String, Option<String>)> {
71    let Some((_, _, nav)) = tags_where(html, |tag, attrs| {
72        tag == "nav" && attr_value(attrs, "aria-label").as_deref() == Some("breadcrumb")
73    })
74    .into_iter()
75    .next() else {
76        return Vec::new();
77    };
78    tags_where(&nav, |tag, _| tag == "li")
79        .into_iter()
80        .filter_map(|(_, _, item)| {
81            let text = strip_text(&item);
82            if text.is_empty() {
83                return None;
84            }
85            let href = open_tags(&item)
86                .into_iter()
87                .find(|(tag, _, _)| tag == "a")
88                .and_then(|(_, attrs, _)| attr_value(&attrs, "href"));
89            Some((text, href))
90        })
91        .collect()
92}
93
94/// The `href` of the tenant menu's link to the page `page` names, `profile` for the tenant
95/// settings or `registration` for a new tenant; `None` when the menu has no such link.
96pub fn tenant_page_link(html: &str, page: &str) -> Option<String> {
97    open_tags(html)
98        .into_iter()
99        .find(|(tag, attrs, _)| {
100            tag == "a" && attr_value(attrs, "data-tenant-page").as_deref() == Some(page)
101        })
102        .and_then(|(_, attrs, _)| attr_value(&attrs, "href"))
103}