1use std::fs;
2use std::path::Path;
3use std::time::{Duration, Instant};
4use serde_json::{json, Value};
5use regex::Regex;
6use crate::{
7 constants::*,
8 default_json::*,
9 utils::*,
10 shared::Shared,
11 block_parser::BlockParser,
12 block_parser::BlockInherit
13};
14
15pub struct Template<'a> {
16 raw: String,
17 file_path: &'a str,
18 schema: Value,
19 shared: Shared,
20 time_start: Instant,
21 time_elapsed: Duration,
22 out: String,
23}
24
25impl<'a> Template<'a> {
29 pub fn new() -> Result<Self, String> {
33 let default_schema: Value = match serde_json::from_str(DEFAULT) {
34 Ok(value) => value,
35 Err(_) => return Err("const DEFAULT is not a valid JSON string".to_string()),
36 };
37 let shared = Shared::new(default_schema.clone());
38
39 Ok(Template {
40 raw: String::new(),
41 file_path: "",
42 schema: default_schema,
43 shared,
44 time_start: Instant::now(),
45 time_elapsed: Instant::now().elapsed(),
46 out: String::new(),
47 })
48 }
49
50 pub fn from_file_value(file_path: &'a str, schema: Value) -> Result<Self, String> {
62 let raw: String = match fs::read_to_string(file_path) {
63 Ok(s) => s,
64 Err(e) => {
65 eprintln!("Cannot be read: {}", file_path);
66 return Err(e.to_string());
67 }
68 };
69 let mut default_schema: Value = match serde_json::from_str(DEFAULT) {
70 Ok(value) => value,
71 Err(_) => {
72 eprintln!("Internal error in const DEFAULT {}, line: {}", file!(), line!());
73 return Err("const DEFAULT is not a valid JSON string".to_string());
74 }
75 };
76
77 update_schema(&mut default_schema, &schema);
78 let shared = Shared::new(default_schema.clone());
79
80 Ok(Template {
81 raw,
82 file_path,
83 schema: default_schema,
84 shared,
85 time_start: Instant::now(),
86 time_elapsed: Instant::now().elapsed(),
87 out: String::new(),
88 })
89 }
90
91 pub fn set_src_path(&mut self, file_path: &'a str) -> Result<(), String> {
101 self.file_path = file_path;
102 self.raw = match fs::read_to_string(file_path) {
103 Ok(s) => s,
104 Err(e) => {
105 eprintln!("Cannot be read: {}", file_path);
106 return Err(e.to_string());
107 }
108 };
109
110 Ok(())
111 }
112
113 pub fn set_src_str(&mut self, source: &str) {
119 self.raw = source.to_string();
120 }
121
122 pub fn merge_schema_path(&mut self, schema_path: &str) -> Result<(), String> {
134 let schema_str: String = match fs::read_to_string(schema_path) {
135 Ok(s) => s,
136 Err(e) => {
137 eprintln!("Cannot be read: {}", schema_path);
138 return Err(e.to_string());
139 }
140 };
141 let schema_value: Value = match serde_json::from_str(&schema_str) {
142 Ok(value) => value,
143 Err(_) => {
144 return Err("Is not a valid JSON file".to_string());
145 }
146 };
147 update_schema(&mut self.schema, &schema_value);
148
149 Ok(())
150 }
151
152 pub fn merge_schema_str(&mut self, schema: &str) -> Result<(), String> {
163 let schema_value: Value = match serde_json::from_str(schema) {
164 Ok(value) => value,
165 Err(_) => {
166 return Err("Is not a valid JSON string".to_string());
167 }
168 };
169 update_schema(&mut self.schema, &schema_value);
170
171 Ok(())
172 }
173
174 pub fn merge_schema_value(&mut self, schema: Value) {
180 update_schema(&mut self.schema, &schema);
181 }
182
183 pub fn render(&mut self) -> String {
192 let inherit = self.init_render();
193 self.out = BlockParser::new(&mut self.shared, &inherit).parse(&self.raw, "");
194
195 while self.out.contains("{:!cache;") {
196 let out;
197 out = BlockParser::new(&mut self.shared, &inherit).parse(&self.out, "!cache");
198 self.out = out;
199 }
200
201 self.ends_render();
202
203 self.out.clone()
204 }
205
206 fn init_render(&mut self) -> BlockInherit {
208 self.time_start = Instant::now();
209 self.shared = Shared::new(self.schema.clone());
210
211 if self.shared.comments.contains("remove") {
212 self.raw = remove_comments(&self.raw);
213 }
214
215 let mut inherit = BlockInherit::new();
217 let indir = inherit.create_block_schema(&mut self.shared);
218 self.shared.schema["__moveto"] = json!({});
219 self.shared.schema["__error"] = json!([]);
220 self.shared.schema["__indir"] = json!({});
221 self.shared.schema["__indir"][&indir] = self.shared.schema["inherit"].clone();
222 inherit.current_file = self.file_path.to_string();
223
224 filter_value(&mut self.shared.schema["data"]["CONTEXT"]);
226
227 filter_value_keys(&mut self.shared.schema["data"]["CONTEXT"]);
229
230 if !self.file_path.is_empty() {
231 let path = Path::new(&self.file_path);
232
233 if let Some(parent) = path.parent() {
234 inherit.current_dir = parent.display().to_string();
235 }
236 } else {
237 inherit.current_dir = self.shared.working_dir.clone();
238 }
239
240 if !self.shared.debug_file.is_empty() {
241 eprintln!("WARNING: config->debug_file is not empty: {} (Remember to remove this in production)", self.shared.debug_file);
242 }
243
244 inherit
245 }
246
247 fn ends_render(&mut self) {
249 self.set_moveto();
250 self.replacements();
251 self.set_status_code();
252 self.time_elapsed = self.time_start.elapsed();
253 }
254
255 fn set_status_code(&mut self) {
256 let status_code = self.shared.status_code.as_str();
257
258 if ("400"..="599").contains(&status_code) {
259 self.out = format!("{} {}", self.shared.status_code, self.shared.status_text);
260
261 return;
262 }
263
264 if status_code == "301"
265 || status_code == "302"
266 || status_code == "303"
267 || status_code == "307"
268 || status_code == "308"
269 {
270 self.out = format!(
271 "{} {}\n{}",
272 self.shared.status_code, self.shared.status_text, self.shared.status_param
273 );
274
275 return;
276 }
277
278 if !self.shared.redirect_js.is_empty() {
279 self.out = self.shared.redirect_js.clone();
280 }
281 }
282
283 fn set_moveto(&mut self) {
284 if let Value::Object(data_map) = &self.shared.schema["__moveto"] {
285 for (_key, value) in data_map {
286 if let Value::Object(inner_map) = value {
287 for (inner_key, inner_value) in inner_map {
288 let mut tag;
289
290 if !inner_key.starts_with("<") {
293 tag = format!("<{}", inner_key);
294 } else {
295 tag = inner_key.to_string();
296 }
297 if tag.ends_with(">") {
298 tag = tag[..tag.len() - 1].to_string();
299 }
300
301 let position = find_tag_position(&self.out, &tag);
303 if let Some(pos) = position {
304 let mut insert = inner_value.as_str().unwrap().to_string();
305 insert = insert.to_string();
306 self.out.insert_str(pos, &insert);
307 }
308 }
309 }
310 }
311 }
312 }
313
314 fn replacements(&mut self) {
315 let pattern = format!(r"\s*{}", BACKSPACE);
316 let re = Regex::new(&pattern).expect("Failed to create regex with constant pattern");
317 self.out = re.replace_all(&self.out, "").to_string();
318
319 self.out = self.out.replace(UNPRINTABLE, "");
321 }
322
323 pub fn get_status_code(&self) -> &String {
333 &self.shared.status_code
334 }
335
336 pub fn get_status_text(&self) -> &String {
344 &self.shared.status_text
345 }
346
347 pub fn get_status_param(&self) -> &String {
357 &self.shared.status_param
358 }
359
360 pub fn has_error(&self) -> bool {
368 self.shared.has_error
369 }
370
371 pub fn get_error(&self) -> Value {
377 self.shared.schema["__error"].clone()
378 }
379
380 pub fn get_time_duration(&self) -> Duration {
386 let duration: std::time::Duration = self.time_elapsed;
387
388 duration
389 }
390}