1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
use crate::Response;
use std::collections::HashMap;
use tokio::fs::File;
use tokio::io::AsyncReadExt;
/// # File Loader
///
/// Loads a html file from a templates folder. Its sole purpose it to load files asynchrynously
pub struct FileLoader;
impl FileLoader {
/// # Load Template
///
/// Loads a template from a given path, returning the loaded file as a `String`
pub async fn load_template(path: &str) -> String {
let mut file_handle = File::open(path).await.unwrap();
let mut contents = vec![];
file_handle.read_to_end(&mut contents).await.unwrap();
String::from_utf8(contents).expect("File not valid UTF-8")
}
}
/// Enum to wrap variables for passing into the HtmlConstructor when constructing
/// a html page
pub enum Variable {
Int(i32),
Float(f32),
UInt(usize),
String(String),
}
/// Holds all variables to dynamically generate (similar to jinja in python)
pub type Vars = HashMap<String, Variable>;
/// # Html Constructor
///
/// Loads a html file and constructs it for dynamic web loading
///
/// For example, when loading a template, you can pass through variables in a vec to implement them in your page
///
/// You can use this for things like user info or other server side information
///
/// Alternatively you could create a static page with JavaScript that loads this information from a webapi route
/// on the same server.
///
/// Dynamic variables are defined as `[ var_name ]` in HTML, and
/// just `var_name` in the Vars hashmap
///
/// Here is an example:
///
/// ```html
/// <p>[ test_var ]</p>
/// ```
///
/// ```rust
/// let mut vars = Vars::new();
/// vars.insert("test_var".to_string(), Variable::String("Test".to_string()));
/// ```
///
/// This example shows a basic example of how the HTML code matches to Rust.
///
/// Please note that if there is no variable defined in the hashmap, it will not update with
/// any dynamic values, and remain static. If the variable in the hashmap doesn't find the variable in the HTML,
/// nothing will happen there as well.
pub struct HtmlConstructor;
impl HtmlConstructor {
/// # Construct Page
///
/// Takes in a file path (to the HTML file) and a `Vars` type.
///
/// Constructs the HTML page, returning a string value (also assigns all dynamic variables if any)
pub async fn construct_page(response_code: Response, path: &str, vars: Vars) -> String {
let file = FileLoader::load_template(path).await;
let file = HtmlConstructor::set_dynamic_vars(file, vars);
// This should be changed over to support all response types
let header_code = match response_code {
Response::Ok => {
format!("HTTP/1.1 {} {}\r\n\r\n", 200, "OK")
}
Response::Redirect => {
format!("HTTP/1.1 {} {}\r\n\r\n", 301, "MOVED PERMANENTLY")
}
Response::ClientErr => {
format!("HTTP/1.1 {} {}\r\n\r\n", 404, "NOT FOUND")
}
Response::ServerErr => {
format!("HTTP/1.1 {} {}\r\n\r\n", 500, "INTERNAL SERVER ERROR")
}
};
let file = format!("{}{}", header_code, file);
return file;
}
/// # Set Dynamic Vars
///
/// Set the dynamic variables in a html file
///
/// Dynamic variables are defined as `[ var_name ]` in HTML, and
/// just `var_name` in the Vars hashmap
fn set_dynamic_vars(mut file: String, vars: Vars) -> String {
for (key, var) in vars.iter() {
let var_to_replace = format!("[ {} ]", key);
match var {
Variable::Int(v) => {
file = file.replace(&var_to_replace, &v.to_string());
}
Variable::Float(v) => {
file = file.replace(&var_to_replace, &v.to_string());
}
Variable::UInt(v) => {
file = file.replace(&var_to_replace, &v.to_string());
}
Variable::String(v) => {
file = file.replace(&var_to_replace, &v);
}
};
}
file
}
}