ddoc/config/
nav_component.rs1use {
2 crate::*,
3 indexmap::IndexMap,
4 serde::{
5 Deserialize,
6 Serialize,
7 },
8};
9
10#[derive(Debug, Clone, Deserialize)]
12pub struct NavComponents {
13 #[serde(default)]
14 header: NavDir,
15 #[serde(default)]
16 footer: NavDir,
17 #[serde(default)]
18 ui: UiOptions,
19}
20
21#[derive(Debug, Clone, Default, Deserialize, Serialize)]
23#[serde(transparent)]
24struct NavDir {
25 components: IndexMap<ClassName, NavComponent>,
26}
27
28#[derive(Debug, Clone, Deserialize, Serialize)]
29#[serde(untagged)]
30enum NavComponent {
31 Menu(Menu),
32 NavLinks(Vec<NavLink>),
33}
34
35impl NavComponents {
36 pub fn into_body_composite(&self) -> ElementList {
39 let mut children = Vec::new();
40 if !self.header.is_empty() {
41 children.push(Element::new_composite(
42 "header",
43 self.header.to_children(&self.ui),
44 ));
45 }
46 children.push(Element::new_composite(
47 "article",
48 vec![
49 Element::new_composite("aside.page-nav", vec![ElementContent::Toc.into()]),
50 ElementContent::Main.into(),
51 ],
52 ));
53 if !self.footer.is_empty() {
54 children.push(Element::new_composite(
55 "footer",
56 self.footer.to_children(&self.ui),
57 ));
58 }
59 ElementList { children }
60 }
61}
62impl NavDir {
63 pub fn is_empty(&self) -> bool {
64 self.components.is_empty()
65 }
66 fn to_children(
67 &self,
68 ui: &UiOptions,
69 ) -> Vec<Element> {
70 let mut children = Vec::new();
71 for (class, comp) in &self.components {
72 match comp {
73 NavComponent::Menu(_) => {
74 let menu_insert = Menu {
75 hamburger_checkbox: ui.hamburger_checkbox,
76 };
77 children.push(Element {
78 classes: vec![class.clone()],
79 content: ElementContent::Menu(menu_insert),
80 });
81 }
82 NavComponent::NavLinks(links) => {
83 let mut nav_children = Vec::new();
84 for link in links {
85 nav_children.push(ElementContent::Link(link.clone()).into());
86 }
87 let mut nav = Element::new_composite("nav", nav_children);
88 nav.classes.push(class.clone());
89 children.push(nav);
90 }
91 }
92 }
93 children
94 }
95}
96
97#[test]
98fn test_nav_component_deserialize() {
99 #[derive(Debug, Clone, Deserialize, Serialize)]
100 struct TestConfig {
101 header: NavDir,
102 footer: NavDir,
103 }
104 let hjson = r#"
105 header: {
106 before-menu: [
107 {
108 img: img/dystroy-rust-white.svg
109 href: https://dystroy.org
110 alt: dystroy.org homepage
111 class: external-nav-link
112 }
113 {
114 url: /index.md
115 alt: ddoc homepage
116 label: ddoc
117 class: home-link
118 }
119 ]
120 middle: menu
121 after-menu: [
122 {
123 img: img/ddoc-left-arrow.svg
124 href: --previous
125 class: previous-page-link
126 alt: Previous Page
127 }
128 {
129 img: img/ddoc-search.svg
130 href: --search
131 class: search-opener
132 alt: Search
133 }
134 {
135 img: img/ddoc-right-arrow.svg
136 url: --next
137 class: next-page-link
138 alt: Next Page
139 }
140 {
141 img: img/github-mark-white.svg
142 class: external-nav-link
143 alt: GitHub
144 href: https://github.com/Canop/ddoc
145 }
146 ]
147 }
148 footer: {
149 right: [
150 {
151 label: made with **ddoc**
152 href: https://dystroy.org/ddoc
153 }
154 ]
155 }
156 "#;
157 let test_config: TestConfig = deser_hjson::from_str(hjson).unwrap();
158 assert_eq!(test_config.header.components.len(), 3);
159 assert_eq!(test_config.footer.components.len(), 1);
160 assert!(matches!(
161 test_config.header.components.get("middle").unwrap(),
162 NavComponent::Menu(_)
163 ));
164 let NavComponent::NavLinks(links) = test_config.header.components.get("after-menu").unwrap()
165 else {
166 panic!("Expected NavLinks");
167 };
168 assert_eq!(links[2].href.as_deref(), Some("--next"));
169}