More refactoring + MD switch
+ Switched to Markdown + Removed RST module and tests + Converted RST pages to MD + Updated a few pages + Clippy + Fmt
This commit is contained in:
parent
33a6056445
commit
813c16a00c
142
Cargo.lock
generated
142
Cargo.lock
generated
@ -41,6 +41,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.3.15"
|
||||
@ -66,6 +75,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@ -250,6 +270,21 @@ dependencies = [
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
@ -440,6 +475,15 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
@ -475,6 +519,15 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.15"
|
||||
@ -622,9 +675,11 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"pulldown-cmark",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
"tera",
|
||||
"tokio",
|
||||
"tower",
|
||||
@ -921,6 +976,30 @@ version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.49"
|
||||
@ -930,6 +1009,18 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"getopts",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
@ -1142,6 +1233,36 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
"structopt-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
@ -1190,6 +1311,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
@ -1432,12 +1562,24 @@ version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
|
@ -22,3 +22,5 @@ tower-http = { version = "0.3.0", features = [
|
||||
] }
|
||||
tower-layer = "0.3.2"
|
||||
axum-extra = { version = "0.4.2", features = ["spa"]}
|
||||
structopt = "0.3.26"
|
||||
pulldown-cmark = "0.9.2"
|
||||
|
7
src/config.rs
Normal file
7
src/config.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use std::net::SocketAddr;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct SiteArgs {
|
||||
pub serve_at: SocketAddr,
|
||||
}
|
@ -44,3 +44,5 @@ impl Display for JSiteError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type PageResult<T> = Result<T, JSiteError>;
|
||||
|
55
src/main.rs
55
src/main.rs
@ -1,8 +1,7 @@
|
||||
mod config;
|
||||
mod error;
|
||||
mod rst_parser;
|
||||
mod tests;
|
||||
|
||||
use crate::rst_parser::parse_images;
|
||||
use axum::error_handling::HandleErrorLayer;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::StatusCode;
|
||||
@ -10,20 +9,21 @@ use axum::response::{Html, IntoResponse};
|
||||
use axum::routing::get;
|
||||
use axum::{BoxError, Router};
|
||||
use axum_extra::routing::SpaRouter;
|
||||
use error::JSiteError;
|
||||
use error::{JSiteError, PageResult};
|
||||
use pulldown_cmark::html::push_html;
|
||||
use pulldown_cmark::{Options, Parser};
|
||||
use regex::Regex;
|
||||
use rst_parser::parse_links;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use structopt::StructOpt;
|
||||
use tera::{Context, Tera};
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
type PageResult<T> = Result<T, JSiteError>;
|
||||
use crate::config::SiteArgs;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SiteFile {
|
||||
@ -39,20 +39,20 @@ struct PageData {
|
||||
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`
|
||||
/// Returns the rendered template of the index page of the website. This includes links and md
|
||||
/// pages included in `static/raw_md`
|
||||
async fn index(State(state): State<Arc<Tera>>) -> PageResult<impl IntoResponse> {
|
||||
let mut ctx = Context::new();
|
||||
let mut links: Vec<SiteFile> = Vec::new();
|
||||
|
||||
// Get the links to display on the main page
|
||||
get_pages("static/raw_rst", &mut links)?;
|
||||
get_pages("static/raw_md", &mut links)?;
|
||||
|
||||
ctx.insert("links", &links);
|
||||
Ok(Html(state.render("index.html.tera", &ctx)?))
|
||||
}
|
||||
|
||||
/// Gets all the raw rst pages contained in a directory
|
||||
/// Gets all the raw md pages contained in a directory
|
||||
///
|
||||
/// The order of the vector is determined by OS. Ordering can be set by prepending the file name
|
||||
/// with a number. Files that start with lower numbers are placed earlier in the list.
|
||||
@ -86,7 +86,7 @@ fn get_pages(path: &str, pages: &mut Vec<SiteFile>) -> PageResult<()> {
|
||||
} else {
|
||||
match rank.parse() {
|
||||
Ok(r) => r,
|
||||
Err(_) => std::u32::MAX,
|
||||
Err(_) => u32::MAX,
|
||||
}
|
||||
};
|
||||
|
||||
@ -149,12 +149,12 @@ fn get_page(path: &std::path::Path) -> PageResult<SiteFile> {
|
||||
Err(JSiteError::PageNotFound(path.to_path_buf()))
|
||||
}
|
||||
|
||||
/// Returns a rendered template of a raw rst page if it exists
|
||||
/// Returns a rendered template of a raw md page if it exists
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `page` - path to page
|
||||
async fn rst_page(tera: State<Arc<Tera>>, Path(page): Path<PathBuf>) -> PageResult<Html<String>> {
|
||||
let mut path = PathBuf::from("static/raw_rst");
|
||||
async fn md_page(tera: State<Arc<Tera>>, Path(page): Path<PathBuf>) -> PageResult<Html<String>> {
|
||||
let mut path = PathBuf::from("static/raw_md");
|
||||
path.push(page);
|
||||
|
||||
// Try and get the page
|
||||
@ -182,35 +182,35 @@ async fn rst_page(tera: State<Arc<Tera>>, Path(page): Path<PathBuf>) -> PageResu
|
||||
map.insert("page_data", &page_data);
|
||||
tera.render("listing.html.tera", &map)?
|
||||
} else {
|
||||
// Else, render the RST page
|
||||
// Else, render the MD page
|
||||
let mut map = Context::new();
|
||||
let contents = match std::fs::read_to_string(site_page.path.clone()) {
|
||||
Ok(contents) => contents,
|
||||
Err(_) => return error_page(&tera, site_page.path.to_str().unwrap()).await,
|
||||
};
|
||||
|
||||
// Render links
|
||||
let mut contents = parse_links(&contents).unwrap();
|
||||
contents = parse_images(contents.as_str()).unwrap();
|
||||
let options = Options::all();
|
||||
let parser = Parser::new_ext(&contents, options);
|
||||
|
||||
// Ensure render will look good
|
||||
contents = contents.replace('\n', "<br>");
|
||||
contents = contents.replace(" ", " ");
|
||||
let mut html_output = String::new();
|
||||
push_html(&mut html_output, parser);
|
||||
|
||||
map.insert("page", &site_page.link_name);
|
||||
map.insert("content", &contents);
|
||||
tera.render("rst_page.html.tera", &map)?
|
||||
map.insert("content", &html_output);
|
||||
tera.render("md_page.html.tera", &map)?
|
||||
};
|
||||
|
||||
Ok(Html(page))
|
||||
}
|
||||
|
||||
/// Build error page
|
||||
async fn error_page(tera: &Tera, page: &str) -> PageResult<Html<String>> {
|
||||
let mut map = Context::new();
|
||||
map.insert("error_page", page);
|
||||
Ok(Html(tera.render("404.html.tera", &map)?))
|
||||
}
|
||||
|
||||
/// Handle server errors
|
||||
async fn handle_error(error: BoxError) -> impl IntoResponse {
|
||||
if error.is::<tower::timeout::error::Elapsed>() {
|
||||
return (StatusCode::REQUEST_TIMEOUT, Cow::from("request timed out"));
|
||||
@ -232,6 +232,8 @@ async fn handle_error(error: BoxError) -> impl IntoResponse {
|
||||
/// Launches website
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let args = SiteArgs::from_args();
|
||||
|
||||
// Use globbing
|
||||
let tera = match Tera::new("templates/*.tera") {
|
||||
Ok(t) => Arc::new(t),
|
||||
@ -243,7 +245,7 @@ async fn main() {
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(index))
|
||||
.route("/about/*path", get(rst_page))
|
||||
.route("/about/*path", get(md_page))
|
||||
.merge(SpaRouter::new("/static", "static"))
|
||||
.layer(
|
||||
ServiceBuilder::new()
|
||||
@ -256,9 +258,8 @@ async fn main() {
|
||||
)
|
||||
.with_state(tera);
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
println!("listening on {}", addr);
|
||||
axum::Server::bind(&addr)
|
||||
println!("listening on {}", args.serve_at);
|
||||
axum::Server::bind(&args.serve_at)
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -1,113 +0,0 @@
|
||||
pub mod rst_error;
|
||||
|
||||
use regex::{Captures, Regex};
|
||||
use rst_error::RSTError;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// RST Image
|
||||
struct RSTImage {
|
||||
/// File location
|
||||
image_file: String,
|
||||
/// Height
|
||||
height: Option<String>,
|
||||
/// Width
|
||||
width: Option<String>,
|
||||
/// Alt Text
|
||||
alt: Option<String>,
|
||||
/// Align (not implemented)
|
||||
#[allow(dead_code)]
|
||||
align: Option<String>,
|
||||
}
|
||||
|
||||
impl RSTImage {
|
||||
/// Convert to HTML img tag
|
||||
fn to_html(&self) -> String {
|
||||
let mut html = format!("<img src=\"{}\"", self.image_file);
|
||||
let mut style = "".to_string();
|
||||
|
||||
if let Some(alt) = &self.alt {
|
||||
html = format!("{} alt=\"{}\"", html, alt);
|
||||
}
|
||||
|
||||
if let Some(height) = &self.height {
|
||||
style = format!("{}height;{};", style, height);
|
||||
}
|
||||
if let Some(width) = &self.width {
|
||||
style = format!("{}width:{};", style, width);
|
||||
}
|
||||
|
||||
html = format!("{} style=\"{}\">", html, style);
|
||||
|
||||
html
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Captures<'_>> for RSTImage {
|
||||
type Error = RSTError;
|
||||
/// Convert from a regex capture to a RSTImage
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * cap - regex capture
|
||||
fn try_from(cap: &Captures<'_>) -> Result<Self, Self::Error> {
|
||||
let image = Self {
|
||||
image_file: cap
|
||||
.name("file_name")
|
||||
.map(|m| m.as_str().to_string())
|
||||
.ok_or(RSTError::NotFound("file_name"))?,
|
||||
height: cap.name("width").map(|m| m.as_str().to_string()),
|
||||
width: cap.name("width").map(|m| m.as_str().to_string()),
|
||||
alt: cap.name("alt").map(|m| m.as_str().to_string()),
|
||||
align: cap.name("align").map(|m| m.as_str().to_string()),
|
||||
};
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders RST links as HTML links
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `string` - input RST string
|
||||
pub fn parse_links(string: &str) -> Result<String, RSTError> {
|
||||
let re_link_ref = Regex::new(r"\n?.. _(.*): (.*)\n")?;
|
||||
let mut link_map: HashMap<String, String> = HashMap::new();
|
||||
|
||||
for cap in re_link_ref.captures_iter(string) {
|
||||
link_map.insert(String::from(&cap[1]), String::from(&cap[2]));
|
||||
}
|
||||
|
||||
let text: String = re_link_ref.replace_all(string, "").to_string();
|
||||
|
||||
let re_link = Regex::new(r"`(.*)`_")?;
|
||||
|
||||
let output = re_link.replace_all(&text, |cap: &Captures| {
|
||||
let link = match link_map.get(&cap[1]) {
|
||||
None => String::from(""),
|
||||
Some(link) => link.to_owned(),
|
||||
};
|
||||
|
||||
format!("<a class=\"link\" href=\"{}\">{}</a>", link, &cap[1])
|
||||
});
|
||||
|
||||
Ok(output.to_string())
|
||||
}
|
||||
|
||||
/// Renders RST images as HTML embedded images
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `string` - input rst string
|
||||
pub fn parse_images(string: &str) -> Result<String, RSTError> {
|
||||
let re_image = Regex::new(
|
||||
r".. image:: (?P<file_name>.*)\n( *((:height: (?P<height>.*))|(:width: (?P<width>.*))|(:scale: (?P<scale>.*%))|(:alt: (?P<alt>.*))|(:align: (?P<align>.*)))\n)*",
|
||||
)?;
|
||||
|
||||
Ok(re_image
|
||||
.replace_all(string, |cap: &Captures| {
|
||||
RSTImage::try_from(cap).unwrap().to_html()
|
||||
})
|
||||
.to_string())
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
use regex::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RSTError {
|
||||
/// Regex compile error
|
||||
RegexError(regex::Error),
|
||||
/// Required element not found
|
||||
NotFound(&'static str),
|
||||
}
|
||||
|
||||
impl std::error::Error for RSTError {}
|
||||
|
||||
impl From<regex::Error> for RSTError {
|
||||
/// From regex error
|
||||
fn from(e: Error) -> Self {
|
||||
Self::RegexError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RSTError {
|
||||
/// fmt error
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
RSTError::NotFound(s) => write!(f, "required element missing: `{}`", s),
|
||||
RSTError::RegexError(e) => write!(f, "regex compile error: `{:?}`", e),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#[cfg(test)]
|
||||
use super::rst_parser::{parse_images, parse_links};
|
||||
|
||||
#[test]
|
||||
fn test_link_parser() {
|
||||
let mut input = String::from(
|
||||
"This is a paragraph that contains `a link`_.
|
||||
|
||||
.. _a link: https://domain.invalida\n",
|
||||
);
|
||||
|
||||
let output = parse_links(&mut input).unwrap();
|
||||
|
||||
assert_eq!(output.trim_end(), "This is a paragraph that contains <a class=\"link\" href=\"https://domain.invalida\">a link</a>.")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_image_parser() {
|
||||
let input = ".. image:: cool/image/123.png
|
||||
:width: 60%
|
||||
:height: auto
|
||||
:alt: this is the alt text
|
||||
";
|
||||
|
||||
let output = parse_images(input).unwrap();
|
||||
|
||||
assert_eq!(output.trim_end(), "<img src=\"cool/image/123.png\" alt=\"this is the alt text\" style=\"height;60%;width:60%;\">")
|
||||
}
|
@ -1,24 +1,22 @@
|
||||
Joey Hines - joey@ahines.net - Westminster, CO
|
||||
==============================================
|
||||
# Joey Hines - joey@ahines.net - Westminster, CO
|
||||
|
||||
Education
|
||||
---------
|
||||
## Education
|
||||
* Masters of Science in Electrical and Computer Engineering
|
||||
* Auburn University, Auburn, AL. GPA: 3.9. May 2021
|
||||
* Bachelors of Science in Computer Engineering
|
||||
* The University of Texas at Dallas, Richardson, TX. GPA: 3.59. May 2019
|
||||
|
||||
Technical Skills
|
||||
----------------
|
||||
## Technical Skills
|
||||
* Programing Languages: C, Python, Rust, Java
|
||||
* Operating Systems: Linux, Windows
|
||||
* Version Control: Git, SVN
|
||||
|
||||
Work Experience
|
||||
---------------
|
||||
## Work Experience
|
||||
Blue Canyon Technologies - Flight Software Engineer - May 2021 to Present
|
||||
* Developed flight software for small satellite missions
|
||||
* Developed flight software for multiple small satellite missions across the BCT product line range
|
||||
* Collaborated with mission operations to improve ground station capabilities
|
||||
* Improved test infrastructure and scripting
|
||||
* Supported the summer internship program as a mentor
|
||||
|
||||
Auburn University - Graduate Research and Teaching Assistant - August 2019 to May 2021
|
||||
* Command and Data Handling (CDHS) team lead for a cube satellite project
|
21
static/raw_md/projects/motion_detector.md
Normal file
21
static/raw_md/projects/motion_detector.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Motion Detector
|
||||
A project made for Auburn ELEC 7450: Digtal Image Processing. The source code can be found on my
|
||||
[GitHub](https://github.com/joeyahines/motion_detector)
|
||||
|
||||
## Goal
|
||||
The goal of this project was to detect motion on a video stream from a [VideoForLinux](https://en.wikipedia.org/wiki/Video4Linux) source.
|
||||
The algorithm can also be tested by loading in individual frames.
|
||||
|
||||
## Implementation
|
||||
The project was written in C and VideoForLinux for grabbing image data and [SDL](https://www.libsdl.org/)
|
||||
for rendering the video output. A background model is built by implementing a moving average of the image.
|
||||
In addition to this, a motion mask is implemented to desensitise the algorithm from background motion objects.
|
||||
|
||||
Webcam output w/ motion highlighted:
|
||||
|
||||

|
||||
|
||||
Motion detection layer:
|
||||
|
||||

|
||||
|
@ -1,5 +1,34 @@
|
||||
This Website
|
||||
============
|
||||
# This Website (V3)
|
||||
|
||||
This is the 3rd iteration of this website. The 1st iteration was a simple HTML page built with bootstrap
|
||||
that looked similar to the current design. The 2nd iteration moved to Rust and the Rocket framework and
|
||||
was more dynamic allowing for RST pages to be rendered to html. This latest iteration uses Axum and Markdown
|
||||
rendered to HTML for the dynamic content.
|
||||
|
||||
## Moving away from Rocket
|
||||
Rocket is a great framework and will probably be the Rust goto. However, it is very complex and comes
|
||||
with a lot of bells and whistles I don't need. Its also in active development which meant it became very
|
||||
hard to update the site. Everytime I wanted to add a new feature I would go to update Rocket and have something
|
||||
break.
|
||||
|
||||
I originally tried Warp, which I have used for some other projects. While I like it mostly, I find it very
|
||||
hard to move past the basic examples. And when you run into issues with it, the compiler loves to spit out
|
||||
very esoteric errors that are a pain to debug.
|
||||
|
||||
I'm also a low level developer. I like working at lower levels of interfaces. Axum is pretty high level
|
||||
in the grand scheme of things, but it doesn't seem overly opinionated in its design. Axum has its own issues
|
||||
and I doubt this will be the last time I switch the framework...
|
||||
|
||||
## Moving away from RST
|
||||
I lied, in the last updated for this site I said how much I loved RST. After using more Markdown for note-taking
|
||||
and for some documentation, I realized I like it more. I always decided to not write my own parse for it.
|
||||
Rust has some good libraries that render MD to HTML.
|
||||
|
||||
# This Website (V2)
|
||||
> NOTE: This is the old version of this page for the second version of this site. It's here for archival purposes.
|
||||
This was originally written in RST and has been converted to MD for v3.
|
||||
|
||||
## 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:
|
||||
@ -10,19 +39,15 @@ improve it, I set out with a few goals:
|
||||
* Improve aesthetics
|
||||
* Ensure mobile version also looks good
|
||||
|
||||
Picking Rust and Rocket
|
||||
-----------------------
|
||||
### 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 went with the `Rocket`_ framework. It
|
||||
I decided to tackle the website backend in Rust as a learning exercise. I 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.
|
||||
|
||||
.. _Rocket: https://rocket.rs/
|
||||
|
||||
Easily Editable
|
||||
---------------
|
||||
### 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.
|
||||
@ -32,8 +57,7 @@ the Rust backend looks for all pages and lists them. To control ordering, I impl
|
||||
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
|
||||
--------------------
|
||||
### Improving Aesthetics
|
||||
Like stated earlier, I am no UI designer. The terminal style meant I could put very little effort into the design of the
|
||||
actual website. Somehow in 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
|
@ -1,33 +0,0 @@
|
||||
Motion Detector
|
||||
===============
|
||||
A project made for Auburn ELEC 7450: Digtal Image Processing. The source code can be found on my `Github`_
|
||||
|
||||
.. _GitHub: https://github.com/joeyahines/motion_detector
|
||||
|
||||
Goal
|
||||
++++
|
||||
The goal of this project was to detect motion on a video stream from a `VideoForLinux`_ source. The algorithm
|
||||
can also be tested by loading in individual frames.
|
||||
|
||||
.. _VideoForLinux: https://en.wikipedia.org/wiki/Video4Linux
|
||||
|
||||
Implementation
|
||||
++++++++++++++
|
||||
The project was written in C and VideoForLinux for grabbing image data and `SDL`_ for rendering the video
|
||||
output. A background model is built by implementing a moving average of the image. In addition to this,
|
||||
a motion mask is implemented to desensitise the algorithm from background motion objects.
|
||||
|
||||
.. _SDL: https://www.libsdl.org/
|
||||
|
||||
Webcam output w/ motion highlighted:
|
||||
|
||||
.. image:: https://github.com/joeyahines/motion_detector/blob/master/docs/final_report/motion.png?&raw=true
|
||||
:width: 60%
|
||||
:height: auto
|
||||
|
||||
Motion detection layer:
|
||||
|
||||
.. image:: https://github.com/joeyahines/motion_detector/blob/master/docs/final_report/motion_image.png?raw=true
|
||||
:width: 60%
|
||||
:height: auto
|
||||
|
@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
.text {
|
||||
font-family: "Ubuntu Mono", monospace;
|
||||
font-family: "Hack", monospace;
|
||||
color: whitesmoke;
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-family: "Ubuntu Mono", monospace;
|
||||
font-family: "Hack", monospace;
|
||||
color: limegreen;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/hack-font/3.3.0/web/hack.min.css" rel="stylesheet">
|
||||
<link rel='shortcut icon' href='/static/logo.png'/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
@ -14,12 +14,12 @@
|
||||
<div class="center">
|
||||
<a class="no_underline" href="/"> <h1 class="heading"> Joey Hines.</h1></a>
|
||||
<div class="terminal">
|
||||
<p class="text body">
|
||||
<div class="text body">
|
||||
<span class="prompt">joey@ahines:~$</span> {% block command %}{% endblock command %} <br/>
|
||||
{% block content %}{% endblock content %}
|
||||
<br/>
|
||||
<span class="prompt">joey@ahines:~$</span> <span class="blinking"> ▊ </span>
|
||||
</p>
|
||||
</div>
|
||||
<span class="prompt text body">joey@ahines:~$</span> <span class="blinking"> ▊ </span>
|
||||
</div>
|
||||
<h6 class="text"> © <script type="text/javascript">document.write( new Date().getFullYear().toString());</script> Joey Hines</h6>
|
||||
</div>
|
||||
|
@ -1,10 +1,11 @@
|
||||
{% extends "base.html.tera" %}
|
||||
|
||||
{% block command %}
|
||||
cat {{ page }}.rst
|
||||
cat {{ page }}.md
|
||||
{% endblock command %}
|
||||
|
||||
|
||||
<div class="text">
|
||||
{% block content %}
|
||||
<br/>
|
||||
{{ content | safe }}
|
Loading…
x
Reference in New Issue
Block a user