diff --git a/src/main.rs b/src/main.rs index aa84046..c3e1c8f 100644 --- a/src/main.rs +++ b/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 = std::result::Result; + +#[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, +} + +/// 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> = HashMap::new(); + let mut links: Vec = Vec::new(); - let mut map: HashMap<&str, Vec> = HashMap::new(); - let mut links: Vec = 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) -> io::Result<()> { - // Gather all of the md files in static/raw_md/ - let mut entries: Vec = fs::read_dir("static/raw_md/")? +fn get_pages(path: &str, links: &mut Vec) -> io::Result<()> { + // Gather all of the rst files in static/raw_rst/ + let mut entries: Vec = fs::read_dir(path)? .map(|res| res.map(|e| e.path())) .collect::, 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) -> 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("/")] -fn md_page(page: String) -> Template { - let mut map = HashMap::new(); - let mut md_files: Vec = Vec::new(); +/// * `path` - path to search in +/// * `page_name` - file to look for +fn get_page(path: &str, page_name: &str) -> Result { + let mut pages: Vec = 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", "
"); - 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/")] +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 = 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", "
"); + 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(); } \ No newline at end of file diff --git a/static/raw_md/1resume.md b/static/raw_rst/1resume.rst similarity index 100% rename from static/raw_md/1resume.md rename to static/raw_rst/1resume.rst diff --git a/static/raw_rst/projects/website.rst b/static/raw_rst/projects/website.rst new file mode 100644 index 0000000..d6a718a --- /dev/null +++ b/static/raw_rst/projects/website.rst @@ -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. \ No newline at end of file diff --git a/templates/404.html.tera b/templates/404.html.tera index b21daf8..021e8bb 100644 --- a/templates/404.html.tera +++ b/templates/404.html.tera @@ -24,5 +24,5 @@ '--' `---` '--'

-
Page: {{ error_page }} Not found...
+
Page "{{ error_page }}" Not found...
{% endblock content %} \ No newline at end of file diff --git a/templates/base.html.tera b/templates/base.html.tera index d201600..7c66b45 100644 --- a/templates/base.html.tera +++ b/templates/base.html.tera @@ -1,8 +1,8 @@ - + Joey Hines. diff --git a/templates/index.html.tera b/templates/index.html.tera index 489de17..90ef0ad 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -5,6 +5,7 @@ {% endblock command %} {% block content %} +
> Aspiring Embedded Software Engineer
> Electrical and Computer Engineering Masters Student at Auburn University
> Graduated from the University of Texas at Dallas with a BS in Computer Engineering
@@ -12,8 +13,9 @@
joey@ahines:~$ ls
+
{% for link in links %} - {{ link.link_name }}   + {{ link.link_name }}   {% endfor %} github   email   diff --git a/templates/listing.html.tera b/templates/listing.html.tera new file mode 100644 index 0000000..be94fcc --- /dev/null +++ b/templates/listing.html.tera @@ -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 %} +
+{{ link.link_name }}   +{% endfor %} +
+{% endblock content %} \ No newline at end of file diff --git a/templates/md_page.html.tera b/templates/rst_page.html.tera similarity index 74% rename from templates/md_page.html.tera rename to templates/rst_page.html.tera index c40ea4c..2a893cd 100644 --- a/templates/md_page.html.tera +++ b/templates/rst_page.html.tera @@ -1,11 +1,11 @@ {% extends "base" %} {% block command %} -cat {{ page }}.md +cat {{ page }}.rst {% endblock command %} {% block content %}
-{{ md_data | safe }} +{{ content | safe }} {% endblock content %} \ No newline at end of file