1use super::dom::{Row, attr_value, columns, elements, rows, strip_text, tags_where};
7
8pub fn widgets(html: &str) -> Vec<String> {
11 tags_where(html, |_, attrs| attr_value(attrs, "data-widget").is_some())
12 .into_iter()
13 .filter_map(|(_, attrs, _)| attr_value(&attrs, "data-widget"))
14 .collect()
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct StatTile {
20 pub label: String,
22 pub value: String,
24 pub href: Option<String>,
26}
27
28pub fn stats(html: &str) -> Vec<StatTile> {
30 tags_where(html, |_, attrs| {
31 attrs.iter().any(|(name, _)| name == "data-stat")
32 })
33 .into_iter()
34 .map(|(tag, attrs, inner)| {
35 let mut paragraphs = elements(&inner, "p")
36 .into_iter()
37 .map(|(_, _, p)| strip_text(&p));
38 StatTile {
39 label: paragraphs.next().unwrap_or_default(),
40 value: paragraphs.next().unwrap_or_default(),
41 href: (tag == "a").then(|| attr_value(&attrs, "href")).flatten(),
42 }
43 })
44 .collect()
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct ChartData {
50 pub kind: String,
52 pub heading: Option<String>,
54 pub labels: Vec<String>,
56 pub series: Vec<ChartSeries>,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct ChartSeries {
63 pub label: String,
65 pub values: Vec<String>,
67}
68
69pub fn charts(html: &str) -> Vec<ChartData> {
71 tags_where(html, |_, attrs| attr_value(attrs, "data-chart").is_some())
72 .into_iter()
73 .map(|(_, attrs, inner)| {
74 let table = tags_where(&inner, |tag, attrs| {
75 tag == "table" && attrs.iter().any(|(name, _)| name == "data-chart-data")
76 })
77 .into_iter()
78 .next()
79 .map(|(_, _, table)| table)
80 .unwrap_or_default();
81 let text = |element: &str| -> Vec<String> {
82 elements(&table, element)
83 .into_iter()
84 .map(|(_, _, inner)| strip_text(&inner))
85 .collect()
86 };
87 let head = elements(&table, "thead")
88 .into_iter()
89 .next()
90 .map(|(_, _, head)| head)
91 .unwrap_or_default();
92 let series = elements(&table, "tbody")
93 .into_iter()
94 .next()
95 .map(|(_, _, body)| elements(&body, "tr"))
96 .unwrap_or_default()
97 .into_iter()
98 .map(|(_, _, row)| ChartSeries {
99 label: elements(&row, "th")
100 .into_iter()
101 .next()
102 .map(|(_, _, th)| strip_text(&th))
103 .unwrap_or_default(),
104 values: elements(&row, "td")
105 .into_iter()
106 .map(|(_, _, td)| strip_text(&td))
107 .collect(),
108 })
109 .collect();
110 ChartData {
111 kind: attr_value(&attrs, "data-chart").unwrap_or_default(),
112 heading: text("caption").into_iter().next(),
113 labels: elements(&head, "th")
114 .into_iter()
115 .map(|(_, _, th)| strip_text(&th))
116 .collect(),
117 series,
118 }
119 })
120 .collect()
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct RecordTableData {
126 pub resource: String,
128 pub heading: String,
130 pub columns: Vec<String>,
132 pub rows: Vec<Row>,
134 pub list: Option<String>,
136}
137
138pub fn record_tables(html: &str) -> Vec<RecordTableData> {
140 tags_where(html, |_, attrs| {
141 attr_value(attrs, "data-record-table").is_some()
142 })
143 .into_iter()
144 .map(|(_, attrs, inner)| RecordTableData {
145 resource: attr_value(&attrs, "data-record-table").unwrap_or_default(),
146 heading: elements(&inner, "h2")
147 .into_iter()
148 .next()
149 .map(|(_, _, h2)| strip_text(&h2))
150 .unwrap_or_default(),
151 columns: columns(&inner),
152 rows: rows(&inner),
153 list: tags_where(&inner, |tag, attrs| {
154 tag == "a"
155 && attrs
156 .iter()
157 .any(|(name, _)| name == "data-record-table-list")
158 })
159 .into_iter()
160 .next()
161 .and_then(|(_, attrs, _)| attr_value(&attrs, "href")),
162 })
163 .collect()
164}