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