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