1use std::collections::HashMap;
2
3use dioxus::{
4 core::{Element, Scope},
5 core_macro::Props,
6};
7
8use reqwasm::http::Request;
9pub use toml::Value;
10pub mod config;
11
12#[derive(Debug, Props, PartialEq)]
13pub struct TemplateProps {
14 pub route: TemplateRouteData,
15 pub data: TemplateData,
16 pub utility: SharedUtility,
17 pub config: HashMap<String, Value>,
18}
19
20#[derive(Debug, Props, PartialEq)]
21pub struct TemplateRouteData {
22 pub bound_path: String,
23 pub access_path: String,
24 pub segments: HashMap<String, String>,
25 pub queries: HashMap<String, String>,
26}
27
28#[derive(Debug, PartialEq)]
29pub struct SharedUtility {
30 pub footer: fn(Scope) -> Element,
32 pub navbar: fn(Scope) -> Element,
34 pub giscus: fn(Scope) -> Element,
36 pub _404: fn(Scope) -> Element,
38 pub error: fn(Scope<ErrorProps>) -> Element,
40 pub renderers: HashMap<String, fn(Scope<RendererProps>) -> Element>,
42 pub app_config: config::Config,
44}
45
46#[derive(Debug, Props, PartialEq)]
47pub struct ErrorProps {
48 pub title: String,
49 pub content: String,
50}
51
52#[derive(Debug, Props, PartialEq)]
53pub struct RendererProps {
54 pub content: String,
55 pub config: HashMap<String, Value>,
56}
57
58#[derive(Debug, Clone, PartialEq)]
60pub enum TemplateData {
61 File(String),
63 Directory(HashMap<String, TemplateData>),
65}
66
67impl TemplateData {
68 pub fn text(&self) -> String {
69 match self {
70 TemplateData::File(content) => content.to_string(),
71 TemplateData::Directory(dir) => format!("{:?}", dir),
72 }
73 }
74 pub fn get(&self, mut index: Vec<String>) -> Option<TemplateData> {
75 if index.is_empty() {
76 return None;
77 }
78 let first = index.remove(0);
79
80 match self {
81 TemplateData::File(_) => {
82 if index.is_empty() {
83 Some(self.clone())
84 } else {
85 None
86 }
87 }
88 TemplateData::Directory(dir) => {
89 if let Some(next) = dir.get(&first) {
90 if index.is_empty() {
91 Some(next.clone())
92 } else {
93 next.get(index)
94 }
95 } else {
96 None
97 }
98 }
99 }
100 }
101}
102
103pub type Component = fn(Scope<TemplateProps>) -> Element;
104
105type TemplatesData = HashMap<TemplateDataType, HashMap<String, Component>>;
106
107#[derive(Debug, Clone)]
108pub struct Templates(TemplatesData);
109impl Templates {
110 pub fn new() -> Self {
111 Self(Default::default())
112 }
113
114 pub fn template(&mut self, name: &str, data_type: Vec<TemplateDataType>, template: Component) {
115 for i in data_type {
116 if self.0.contains_key(&i) {
117 let t = self.0.get_mut(&i).unwrap();
118 t.insert(name.to_string(), template);
119 } else {
120 let mut t = HashMap::new();
121 t.insert(name.to_string(), template);
122 self.0.insert(i, t);
123 }
124 }
125 }
126
127 pub fn sub_module(&mut self, name: &str, templates: Self) {
128 for (k, i) in templates.0 {
129 for j in i {
130 self.template(&format!("{name}::{}", j.0), vec![k.clone()], j.1);
131 }
132 }
133 }
134
135 pub fn load(&self, name: &str, data_type: TemplateDataType) -> Option<&Component> {
136 if self.0.contains_key(&data_type) {
137 let part = self.0.get(&data_type).unwrap();
138 part.get(name)
139 } else {
140 None
141 }
142 }
143}
144
145#[derive(Debug, Eq, Hash, PartialEq, Clone)]
146pub enum TemplateDataType {
147 Markdown,
148 HTML,
149 Json,
150
151 None,
152 DirectoryData,
153 Any,
154 Other(String),
155}
156
157impl TemplateDataType {
158 pub fn to_string(&self) -> String {
159 match self {
160 TemplateDataType::Markdown => "md",
161 TemplateDataType::HTML => "html",
162 TemplateDataType::Json => "json",
163
164 TemplateDataType::None => "",
165 TemplateDataType::DirectoryData => "#dir",
166 TemplateDataType::Any => "*",
167 TemplateDataType::Other(s) => s,
168 }
169 .to_string()
170 }
171
172 pub fn from_string(s: &str) -> Self {
173 match s {
174 "md" => TemplateDataType::Markdown,
175 "html" => TemplateDataType::HTML,
176 "json" => TemplateDataType::Json,
177
178 "" => TemplateDataType::None,
179 "#dir" => TemplateDataType::DirectoryData,
180 "*" => TemplateDataType::Any,
181 _ => Self::Other(s.to_string()),
182 }
183 }
184}
185
186#[derive(Debug, Clone, PartialEq)]
187pub struct LazyLoader(String);
188impl LazyLoader {
189 pub async fn load(self) -> Result<String, ErrorProps> {
190 let url = self.0;
191 let resp = Request::get(&url).send().await;
192 if resp.is_err() {
193 return Err(ErrorProps {
194 title: "content load failed".to_string(),
195 content: format!("lazy loader load content `{}` failed.", url),
196 });
197 } else {
198 return Ok(resp.unwrap().text().await.unwrap());
199 }
200 }
201}