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