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 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
 | 
			
		||||
/// # 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) {
 | 
			
		||||
      Err(_) => (),
 | 
			
		||||
    // Get pages
 | 
			
		||||
    match get_pages(path, &mut pages) {
 | 
			
		||||
        Err(_) => return Err(PageNotFoundError),
 | 
			
		||||
        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();
 | 
			
		||||
    // Look for the page in the directory
 | 
			
		||||
    for page in pages {
 | 
			
		||||
        if page.link_name.eq_ignore_ascii_case(page_name) {
 | 
			
		||||
            return Ok(page)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut contents = match fs::read_to_string(format!("static/raw_md/{}.md", file_name)) {
 | 
			
		||||
        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)
 | 
			
		||||
    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 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)]
 | 
			
		||||
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