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
					
				
							
								
								
									
										151
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -10,22 +10,49 @@ use rocket_contrib::templates::Template; | ||||
| use rocket_contrib::serve::StaticFiles; | ||||
| use std::{fs, io}; | ||||
| use std::path::PathBuf; | ||||
| use std::error; | ||||
| use std::fmt; | ||||
| 
 | ||||
| #[derive(Serialize)] | ||||
| struct MDFile { | ||||
|     file_name: String, | ||||
|     link_name: String, | ||||
| type PageResult<T> = std::result::Result<T, PageNotFoundError>; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| 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
 | ||||
| /// pages included in `static/raw_md`
 | ||||
| impl error::Error for PageNotFoundError { | ||||
|     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("/")] | ||||
| 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(); | ||||
|     let mut links: Vec<MDFile> = Vec::new(); | ||||
| 
 | ||||
|     match get_pages(&mut links) { | ||||
|     // Get the links to display on the main page
 | ||||
|     match get_pages("static/raw_rst", &mut links) { | ||||
|        Err(_) => (), | ||||
|         Ok(_) => (), | ||||
|     } | ||||
| @ -34,23 +61,23 @@ fn index() -> Template { | ||||
|     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
 | ||||
| ///
 | ||||
| /// * `links` - A reference to a vector of string to insert the links into
 | ||||
| fn get_pages(links: &mut Vec<MDFile>) -> io::Result<()> { | ||||
|     // Gather all of the md files in static/raw_md/
 | ||||
|     let mut entries: Vec<PathBuf> =  fs::read_dir("static/raw_md/")? | ||||
| fn get_pages(path: &str, links: &mut Vec<SiteFile>) -> io::Result<()> { | ||||
|     // Gather all of the rst files in static/raw_rst/
 | ||||
|     let mut entries: Vec<PathBuf> =  fs::read_dir(path)? | ||||
|         .map(|res| res.map(|e| e.path())) | ||||
|         .collect::<Result<Vec<_>, io::Error>>()?; | ||||
| 
 | ||||
|     // Sort so they are always in the same order
 | ||||
|     entries.sort(); | ||||
| 
 | ||||
|     //
 | ||||
|     // Find all files in the directory
 | ||||
|     for entry in entries { | ||||
|         let file_name = entry.file_stem().unwrap().to_str().unwrap(); | ||||
|         let link_name; | ||||
| @ -61,55 +88,105 @@ fn get_pages(links: &mut Vec<MDFile>) -> io::Result<()> { | ||||
|             link_name = file_name; | ||||
|         } | ||||
| 
 | ||||
|         let md_file = MDFile { | ||||
|         let rst_file = SiteFile { | ||||
|             file_name: String::from(file_name), | ||||
|             link_name: String::from(link_name), | ||||
|             path: entry.to_owned() | ||||
|         }; | ||||
| 
 | ||||
|         links.push(md_file); | ||||
|         links.push(rst_file); | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| /// Returns a rendered template of a raw md page if it exists
 | ||||
| /// Gets a page matching `page_name` in directory `path`
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `page` - a string containing the name of the md file to look for
 | ||||
| #[get("/<page>")] | ||||
| fn md_page(page: String) -> Template { | ||||
|     let mut map = HashMap::new(); | ||||
|     let mut md_files: Vec<MDFile> = Vec::new(); | ||||
| /// * `path` - path to search in
 | ||||
| /// * `page_name` - file to look for
 | ||||
| fn get_page(path: &str, page_name: &str) -> Result<SiteFile, PageNotFoundError> { | ||||
|     let mut pages: Vec<SiteFile> = Vec::new(); | ||||
| 
 | ||||
|     match get_pages(&mut md_files) { | ||||
|     // Get pages
 | ||||
|     match get_pages(path, &mut pages) { | ||||
|         Err(_) => return Err(PageNotFoundError), | ||||
|         Ok(_) => (), | ||||
|     }; | ||||
| 
 | ||||
|     // Look for the page in the directory
 | ||||
|     for page in pages { | ||||
|         if page.link_name.eq_ignore_ascii_case(page_name) { | ||||
|             return Ok(page) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Err(PageNotFoundError) | ||||
| } | ||||
| 
 | ||||
| /// 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 mut file_name = String::new(); | ||||
|     for md_file in md_files { | ||||
|         if md_file.link_name.eq_ignore_ascii_case(page.as_str()) { | ||||
|             file_name = md_file.file_name.clone(); | ||||
|         } | ||||
|     } | ||||
|         let page_data = PageData { | ||||
|             links: sub_files, | ||||
|             site_file: site_page | ||||
|         }; | ||||
| 
 | ||||
|     let mut contents = match fs::read_to_string(format!("static/raw_md/{}.md", file_name)) { | ||||
|         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", page); | ||||
|     map.insert("md_data", contents); | ||||
|     Template::render("md_page", &map) | ||||
|         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)] | ||||
| fn not_found(req: &Request<'_>) -> Template { | ||||
|     let mut map = HashMap::new(); | ||||
| @ -120,14 +197,16 @@ fn not_found(req: &Request<'_>) -> Template { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /// Launches website
 | ||||
| fn rocket() -> rocket::Rocket { | ||||
|     rocket::ignite() | ||||
|         .mount("/", routes![index, md_page], ) | ||||
|         .mount("/", routes![index, rst_page], ) | ||||
|         .mount("/static", StaticFiles::from("static")) | ||||
|         .attach(Template::fairing()) | ||||
|         .register(catchers![not_found]) | ||||
| } | ||||
| 
 | ||||
| /// Main
 | ||||
| fn main() { | ||||
|     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> | ||||
| <p class="text body"> | ||||
| <br>Page: {{ error_page }} Not found...<br> | ||||
| <br>Page "{{ error_page }}" Not found...<br> | ||||
| {% endblock content %} | ||||
| @ -1,8 +1,8 @@ | ||||
| 
 | ||||
| <!DOCTYPE html> | ||||
| <link rel="stylesheet" type="text/css" href="/static/style.css" xmlns="http://www.w3.org/1999/html"> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <link rel="stylesheet" type="text/css" href="/static/style.css" xmlns="http://www.w3.org/1999/html"> | ||||
|     <meta charset="UTF-8"> | ||||
|     <title>Joey Hines.</title> | ||||
|     <link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono&display=swap" rel="stylesheet"> | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| {% endblock command %} | ||||
| 
 | ||||
| {% block content %} | ||||
|     <br/> | ||||
|     > Aspiring Embedded Software Engineer <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/> | ||||
| @ -12,8 +13,9 @@ | ||||
|     <br/> | ||||
| 
 | ||||
|     <span class="prompt">joey@ahines:~$</span> ls <br/> | ||||
|     <br/> | ||||
|     {% 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 %} | ||||
|     <a class="link" href="https://www.github.com/joeyahines"> github</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" %} | ||||
| 
 | ||||
| {% block command %} | ||||
| cat {{ page }}.md | ||||
| cat {{ page }}.rst | ||||
| {% endblock command %} | ||||
| 
 | ||||
| 
 | ||||
| {% block content %} | ||||
| <br/> | ||||
| {{ md_data | safe }} | ||||
| {{ content | safe }} | ||||
| {% endblock content %} | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user