Added the ability to have directory listings and moved to rst
+ Added projects/website.rst to describe the site + Updated templates + Added more comments
This commit is contained in:
		
							parent
							
								
									5e161f9b35
								
							
						
					
					
						commit
						26575adb87
					
				
							
								
								
									
										171
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -10,22 +10,49 @@ use rocket_contrib::templates::Template; | |||||||
| use rocket_contrib::serve::StaticFiles; | use rocket_contrib::serve::StaticFiles; | ||||||
| use std::{fs, io}; | use std::{fs, io}; | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
|  | use std::error; | ||||||
|  | use std::fmt; | ||||||
| 
 | 
 | ||||||
| #[derive(Serialize)] | type PageResult<T> = std::result::Result<T, PageNotFoundError>; | ||||||
| struct MDFile { | 
 | ||||||
|     file_name: String, | #[derive(Clone, Debug)] | ||||||
|     link_name: String, | struct PageNotFoundError; | ||||||
|  | 
 | ||||||
|  | impl fmt::Display for PageNotFoundError { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         write!(f, "Page not found") | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Returns the rendered template of the index page of the website. This includes links and md
 | impl error::Error for PageNotFoundError { | ||||||
| /// pages included in `static/raw_md`
 |     fn source(&self) -> Option<&(dyn error::Error + 'static)> { | ||||||
|  |         None | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #[derive(Serialize)] | ||||||
|  | struct SiteFile { | ||||||
|  |     file_name: String, | ||||||
|  |     link_name: String, | ||||||
|  |     path: PathBuf, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Serialize)] | ||||||
|  | struct PageData { | ||||||
|  |     site_file: SiteFile, | ||||||
|  |     links: Vec<SiteFile>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Returns the rendered template of the index page of the website. This includes links and rst
 | ||||||
|  | /// pages included in `static/raw_rst`
 | ||||||
| #[get("/")] | #[get("/")] | ||||||
| fn index() -> Template { | fn index() -> Template { | ||||||
|  |     let mut map: HashMap<&str, Vec<SiteFile>> = HashMap::new(); | ||||||
|  |     let mut links: Vec<SiteFile> = Vec::new(); | ||||||
| 
 | 
 | ||||||
|     let mut map: HashMap<&str, Vec<MDFile>> = HashMap::new(); |     // Get the links to display on the main page
 | ||||||
|     let mut links: Vec<MDFile> = Vec::new(); |     match get_pages("static/raw_rst", &mut links) { | ||||||
| 
 |  | ||||||
|     match get_pages(&mut links) { |  | ||||||
|        Err(_) => (), |        Err(_) => (), | ||||||
|         Ok(_) => (), |         Ok(_) => (), | ||||||
|     } |     } | ||||||
| @ -34,23 +61,23 @@ fn index() -> Template { | |||||||
|     Template::render("index", &map) |     Template::render("index", &map) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Gets all the raw md pages contained in static/raw_md/
 | /// Gets all the raw rst pages contained in static/raw_rst/
 | ||||||
| ///
 | ///
 | ||||||
| /// The md page can start with a number
 | /// The rst page can start with a number
 | ||||||
| ///
 | ///
 | ||||||
| /// # Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// * `links` - A reference to a vector of string to insert the links into
 | /// * `links` - A reference to a vector of string to insert the links into
 | ||||||
| fn get_pages(links: &mut Vec<MDFile>) -> io::Result<()> { | fn get_pages(path: &str, links: &mut Vec<SiteFile>) -> io::Result<()> { | ||||||
|     // Gather all of the md files in static/raw_md/
 |     // Gather all of the rst files in static/raw_rst/
 | ||||||
|     let mut entries: Vec<PathBuf> =  fs::read_dir("static/raw_md/")? |     let mut entries: Vec<PathBuf> =  fs::read_dir(path)? | ||||||
|         .map(|res| res.map(|e| e.path())) |         .map(|res| res.map(|e| e.path())) | ||||||
|         .collect::<Result<Vec<_>, io::Error>>()?; |         .collect::<Result<Vec<_>, io::Error>>()?; | ||||||
| 
 | 
 | ||||||
|     // Sort so they are always in the same order
 |     // Sort so they are always in the same order
 | ||||||
|     entries.sort(); |     entries.sort(); | ||||||
| 
 | 
 | ||||||
|     //
 |     // Find all files in the directory
 | ||||||
|     for entry in entries { |     for entry in entries { | ||||||
|         let file_name = entry.file_stem().unwrap().to_str().unwrap(); |         let file_name = entry.file_stem().unwrap().to_str().unwrap(); | ||||||
|         let link_name; |         let link_name; | ||||||
| @ -61,55 +88,105 @@ fn get_pages(links: &mut Vec<MDFile>) -> io::Result<()> { | |||||||
|             link_name = file_name; |             link_name = file_name; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let md_file = MDFile { |         let rst_file = SiteFile { | ||||||
|             file_name: String::from(file_name), |             file_name: String::from(file_name), | ||||||
|             link_name: String::from(link_name), |             link_name: String::from(link_name), | ||||||
|  |             path: entry.to_owned() | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         links.push(md_file); |         links.push(rst_file); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Returns a rendered template of a raw md page if it exists
 | /// Gets a page matching `page_name` in directory `path`
 | ||||||
| ///
 | ///
 | ||||||
| /// #Arguments
 | /// # Arguments
 | ||||||
| ///
 | ///
 | ||||||
| /// * `page` - a string containing the name of the md file to look for
 | /// * `path` - path to search in
 | ||||||
| #[get("/<page>")] | /// * `page_name` - file to look for
 | ||||||
| fn md_page(page: String) -> Template { | fn get_page(path: &str, page_name: &str) -> Result<SiteFile, PageNotFoundError> { | ||||||
|     let mut map = HashMap::new(); |     let mut pages: Vec<SiteFile> = Vec::new(); | ||||||
|     let mut md_files: Vec<MDFile> = Vec::new(); |  | ||||||
| 
 | 
 | ||||||
|     match get_pages(&mut md_files) { |     // Get pages
 | ||||||
|       Err(_) => (), |     match get_pages(path, &mut pages) { | ||||||
|  |         Err(_) => return Err(PageNotFoundError), | ||||||
|         Ok(_) => (), |         Ok(_) => (), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let mut file_name = String::new(); |     // Look for the page in the directory
 | ||||||
|     for md_file in md_files { |     for page in pages { | ||||||
|         if md_file.link_name.eq_ignore_ascii_case(page.as_str()) { |         if page.link_name.eq_ignore_ascii_case(page_name) { | ||||||
|             file_name = md_file.file_name.clone(); |             return Ok(page) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let mut contents = match fs::read_to_string(format!("static/raw_md/{}.md", file_name)) { |     Err(PageNotFoundError) | ||||||
|         Ok(contents) => contents, |  | ||||||
|         Err(_) => { |  | ||||||
|             map.insert("error_page", page); |  | ||||||
|             return Template::render("404", map) |  | ||||||
|         }, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     contents = contents.replace("\n", "<br>"); |  | ||||||
|     contents = contents.replace("  ", "  "); |  | ||||||
| 
 |  | ||||||
|     map.insert("page", page); |  | ||||||
|     map.insert("md_data", contents); |  | ||||||
|     Template::render("md_page", &map) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Returns a rendered template of a raw rst page if it exists
 | ||||||
|  | ///
 | ||||||
|  | /// # Arguments
 | ||||||
|  | ///
 | ||||||
|  | /// * `page` - a string containing the name of the rst file to look for
 | ||||||
|  | #[get("/about/<page..>")] | ||||||
|  | fn rst_page(page: PathBuf) -> Template { | ||||||
|  |     // Try and get the page
 | ||||||
|  |     let site_page = match get_page(format!("static/raw_rst/{}", page.parent().unwrap().to_str().unwrap()).as_str(),  &page.file_name().unwrap().to_str().unwrap()) { | ||||||
|  |         Ok(site_page) => site_page, | ||||||
|  |         Err(_) => { | ||||||
|  |             let mut map = HashMap::new(); | ||||||
|  |             map.insert("error_page", page); | ||||||
|  |             return Template::render("404", map) | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if site_page.path.is_dir() { | ||||||
|  |         // If the file is a directory, list its contents instead
 | ||||||
|  |         let mut map = HashMap::new(); | ||||||
|  |         let mut sub_files: Vec<SiteFile> = Vec::new(); | ||||||
|  |         match get_pages(site_page.path.to_str().unwrap(), &mut sub_files) { | ||||||
|  |             Err(_) => (), | ||||||
|  |             Ok(_) => (), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let page_data = PageData { | ||||||
|  |             links: sub_files, | ||||||
|  |             site_file: site_page | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         map.insert("page_data", page_data); | ||||||
|  |         return Template::render("listing", &map); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         // Else, render the RST page
 | ||||||
|  |         let mut map = HashMap::new(); | ||||||
|  |         let mut contents = match fs::read_to_string(site_page.path) { | ||||||
|  |             Ok(contents) => contents, | ||||||
|  |             Err(_) => { | ||||||
|  |                 let mut map = HashMap::new(); | ||||||
|  |                 map.insert("error_page", page); | ||||||
|  |                 return Template::render("404", map) | ||||||
|  |             }, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // Ensure render will look good
 | ||||||
|  |         contents = contents.replace("\n", "<br>"); | ||||||
|  |         contents = contents.replace("  ", "  "); | ||||||
|  | 
 | ||||||
|  |         map.insert("page", site_page.link_name); | ||||||
|  |         map.insert("content", contents); | ||||||
|  |         Template::render("rst_page", &map) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Catches 404 errors and displays an error message
 | ||||||
|  | ///
 | ||||||
|  | /// #Arguments
 | ||||||
|  | ///
 | ||||||
|  | /// * `req` - information on the original request
 | ||||||
| #[catch(404)] | #[catch(404)] | ||||||
| fn not_found(req: &Request<'_>) -> Template { | fn not_found(req: &Request<'_>) -> Template { | ||||||
|     let mut map = HashMap::new(); |     let mut map = HashMap::new(); | ||||||
| @ -120,14 +197,16 @@ fn not_found(req: &Request<'_>) -> Template { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /// Launches website
 | ||||||
| fn rocket() -> rocket::Rocket { | fn rocket() -> rocket::Rocket { | ||||||
|     rocket::ignite() |     rocket::ignite() | ||||||
|         .mount("/", routes![index, md_page], ) |         .mount("/", routes![index, rst_page], ) | ||||||
|         .mount("/static", StaticFiles::from("static")) |         .mount("/static", StaticFiles::from("static")) | ||||||
|         .attach(Template::fairing()) |         .attach(Template::fairing()) | ||||||
|         .register(catchers![not_found]) |         .register(catchers![not_found]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Main
 | ||||||
| fn main() { | fn main() { | ||||||
|     rocket().launch(); |     rocket().launch(); | ||||||
| } | } | ||||||
							
								
								
									
										45
									
								
								static/raw_rst/projects/website.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								static/raw_rst/projects/website.rst
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | This Website | ||||||
|  | ============ | ||||||
|  | This is the 2nd version of my personal website, the first version was very similar to this but written in plain HTML | ||||||
|  | using Bootstrap. I was never very happy with how it looked and it being raw HTML limited what I could do with it. To | ||||||
|  | improve it, I set out with a few goals: | ||||||
|  | 
 | ||||||
|  | * Use a web framework to improve capabilities | ||||||
|  | * Be easy to deploy | ||||||
|  | * Allow things, like the resume, to be easily editable | ||||||
|  | * Improve aesthetics | ||||||
|  | * Ensure mobile version also looks good | ||||||
|  | 
 | ||||||
|  | Picking Rust and Rocket | ||||||
|  | ----------------------- | ||||||
|  | As I am not much of a UI person, the website update was put on the back burner for sometime. The old site was "good  | ||||||
|  | enough." I originally considered doing it in Python and using Django. But that fails the criteria of being easily | ||||||
|  | deployable. In early 2020, I began learning Rust out of curiosity and the need to use it for research.  | ||||||
|  | I decided to tackle the website backend in Rust as a learning exercise. I then went with the Rocket framework. It  | ||||||
|  | seemed to suit my needs and supported Tera templates. Tera templates are very similar to Django's templates that I | ||||||
|  | had already had experience in. | ||||||
|  | 
 | ||||||
|  | Easily Editable | ||||||
|  | --------------- | ||||||
|  | As the style of the site is a simplistic linux terminal, I decided it would be nice to have the longer pages be in | ||||||
|  | raw reStructuredText. This fits the aesthetic of the site well and unlike HTML files, they are raw text and are easy to | ||||||
|  | write and read while editing them. RST files can also easily be built into PDFs, which is great for generating a resume. | ||||||
|  | 
 | ||||||
|  | To create new pages, I simply have to add new .rst files to a directory on the website. When the templates are rendered, | ||||||
|  | the Rust backend looks for all pages and lists them. To control ordering, I implemented a ranking system where the first | ||||||
|  | character of a file name is its rank. For example, my resume is in a file called 1resume.rst. This gives it rank of | ||||||
|  | 1 so it shows up on the main page before other links. | ||||||
|  | 
 | ||||||
|  | Improving Aesthetics | ||||||
|  | -------------------- | ||||||
|  | Like stated early, I am no UI designer. The terminal style meant I could put very little effort into the design of the | ||||||
|  | actual website. Somehow on version one, I still managed to mess that up. This was mainly due to hacking together  | ||||||
|  | bootstrap to give me something that looked like a terminal. This time, I dumped Bootstrap and wrote my own CSS.  I should | ||||||
|  | have done that the first place as it was easy to do and produced a far better result. This also by side effect improved | ||||||
|  | on the site looked on mobile. | ||||||
|  | 
 | ||||||
|  | Future Improvements | ||||||
|  | ------------------- | ||||||
|  | As I continue to learn Rust, I plan to implement more features of RST into the raw file displays. For example, doing | ||||||
|  | links. I want to strike a balance between the aesthetic of a terminal and usefulness. Being able to embedded images | ||||||
|  | would be nice. | ||||||
| @ -24,5 +24,5 @@ | |||||||
|       '--'      `---`           '--' |       '--'      `---`           '--' | ||||||
| </pre> | </pre> | ||||||
| <p class="text body"> | <p class="text body"> | ||||||
| <br>Page: {{ error_page }} Not found...<br> | <br>Page "{{ error_page }}" Not found...<br> | ||||||
| {% endblock content %} | {% endblock content %} | ||||||
| @ -1,8 +1,8 @@ | |||||||
| 
 | 
 | ||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <link rel="stylesheet" type="text/css" href="/static/style.css" xmlns="http://www.w3.org/1999/html"> |  | ||||||
| <html lang="en"> | <html lang="en"> | ||||||
| <head> | <head> | ||||||
|  |     <link rel="stylesheet" type="text/css" href="/static/style.css" xmlns="http://www.w3.org/1999/html"> | ||||||
|     <meta charset="UTF-8"> |     <meta charset="UTF-8"> | ||||||
|     <title>Joey Hines.</title> |     <title>Joey Hines.</title> | ||||||
|     <link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono&display=swap" rel="stylesheet"> |     <link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono&display=swap" rel="stylesheet"> | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| {% endblock command %} | {% endblock command %} | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
|  |     <br/> | ||||||
|     > Aspiring Embedded Software Engineer <br/> |     > Aspiring Embedded Software Engineer <br/> | ||||||
|     > Electrical and Computer Engineering Masters Student at Auburn University <br/> |     > Electrical and Computer Engineering Masters Student at Auburn University <br/> | ||||||
|     > Graduated from the University of Texas at Dallas with a BS in Computer Engineering <br/> |     > Graduated from the University of Texas at Dallas with a BS in Computer Engineering <br/> | ||||||
| @ -12,8 +13,9 @@ | |||||||
|     <br/> |     <br/> | ||||||
| 
 | 
 | ||||||
|     <span class="prompt">joey@ahines:~$</span> ls <br/> |     <span class="prompt">joey@ahines:~$</span> ls <br/> | ||||||
|  |     <br/> | ||||||
|     {% for link in links %} |     {% for link in links %} | ||||||
|     <a class="link" href="/{{ link.link_name }}">{{ link.link_name }}</a>   |     <a class="link" href="about/{{ link.link_name }}">{{ link.link_name }}</a>   | ||||||
|     {% endfor %} |     {% endfor %} | ||||||
|     <a class="link" href="https://www.github.com/joeyahines"> github</a>   |     <a class="link" href="https://www.github.com/joeyahines"> github</a>   | ||||||
|     <a class="link" href="mailto:joey@ahines.net"> email</a>   |     <a class="link" href="mailto:joey@ahines.net"> email</a>   | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								templates/listing.html.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								templates/listing.html.tera
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | {% extends "base" %} | ||||||
|  | 
 | ||||||
|  | {% block command %} | ||||||
|  | ls {{ page_data.site_file.link_name }} | ||||||
|  | {% endblock command %} | ||||||
|  | 
 | ||||||
|  | {% block content %} | ||||||
|  | {% for link in page_data.links %} | ||||||
|  | <br> | ||||||
|  | <a class="link" href="{{ page_data.site_file.link_name}}/{{ link.link_name }}">{{ link.link_name }}</a>   | ||||||
|  | {% endfor %} | ||||||
|  | <br> | ||||||
|  | {% endblock content %} | ||||||
| @ -1,11 +1,11 @@ | |||||||
| {% extends "base" %} | {% extends "base" %} | ||||||
| 
 | 
 | ||||||
| {% block command %} | {% block command %} | ||||||
| cat {{ page }}.md | cat {{ page }}.rst | ||||||
| {% endblock command %} | {% endblock command %} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
| <br/> | <br/> | ||||||
| {{ md_data | safe }} | {{ content | safe }} | ||||||
| {% endblock content %} | {% endblock content %} | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user