Updated error handling and made more idiomatic
+ Most unwraps removed + Better use of Rust constructs
This commit is contained in:
parent
e88d9d3179
commit
6733076068
187
src/main.rs
187
src/main.rs
@ -1,22 +1,26 @@
|
|||||||
#![feature(proc_macro_hygiene, decl_macro)]
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
#[macro_use] extern crate rocket;
|
#[macro_use]
|
||||||
#[macro_use] extern crate serde_derive;
|
extern crate rocket;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
mod tests;
|
|
||||||
mod rst_parser;
|
mod rst_parser;
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
use rocket::Request;
|
||||||
|
use rocket_contrib::serve::StaticFiles;
|
||||||
|
use rocket_contrib::templates::Template;
|
||||||
use rst_parser::parse_links;
|
use rst_parser::parse_links;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use rocket::Request;
|
|
||||||
use rocket_contrib::templates::Template;
|
|
||||||
use rocket_contrib::serve::StaticFiles;
|
|
||||||
use std::{fs, io};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::path::{PathBuf, Path};
|
||||||
|
use std::{fs, io};
|
||||||
|
use std::io::Error;
|
||||||
|
|
||||||
type PageResult<T> = std::result::Result<T, PageNotFoundError>;
|
type PageResult<T> = std::result::Result<T, JSiteError>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct PageNotFoundError;
|
struct PageNotFoundError;
|
||||||
@ -33,8 +37,27 @@ impl error::Error for PageNotFoundError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum JSiteError {
|
||||||
|
PageNotFound(PageNotFoundError),
|
||||||
|
IOError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<PageNotFoundError> for JSiteError {
|
||||||
|
fn from(e: PageNotFoundError) -> Self {
|
||||||
|
JSiteError::PageNotFound(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<std::io::Error> for JSiteError {
|
||||||
|
fn from(_: Error) -> Self {
|
||||||
|
JSiteError::IOError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct SiteFile {
|
struct SiteFile {
|
||||||
|
rank: u32,
|
||||||
file_name: String,
|
file_name: String,
|
||||||
link_name: String,
|
link_name: String,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
@ -63,41 +86,56 @@ fn index() -> Template {
|
|||||||
Template::render("index", &map)
|
Template::render("index", &map)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets all the raw rst pages contained in static/raw_rst/
|
/// Gets all the raw rst pages contained in a directory
|
||||||
///
|
///
|
||||||
/// The rst page can start with a number
|
/// 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.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
/// * `path` - the path to look for pages in
|
||||||
/// * `links` - A reference to a vector of string to insert the links into
|
/// * `pages` - A vector where found pages will be inserted
|
||||||
fn get_pages(path: &str, links: &mut Vec<SiteFile>) -> io::Result<()> {
|
fn get_pages(path: &str, pages: &mut Vec<SiteFile>) -> io::Result<()> {
|
||||||
// Gather all of the rst files in static/raw_rst/
|
let re = Regex::new(r"(?P<rank>^\d*)(?P<link_name>.+)").unwrap();
|
||||||
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
|
// Find all files in the directory
|
||||||
for entry in entries {
|
for entry in fs::read_dir(path)? {
|
||||||
let file_name = entry.file_stem().unwrap().to_str().unwrap();
|
let entry = entry?;
|
||||||
let link_name;
|
let path = entry.path();
|
||||||
if file_name.chars().next().unwrap().is_numeric() {
|
let file_name = match path.file_stem() {
|
||||||
link_name = &file_name[1..];
|
Some(name) => name,
|
||||||
}
|
None => continue,
|
||||||
else {
|
|
||||||
link_name = file_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rst_file = SiteFile {
|
|
||||||
file_name: String::from(file_name),
|
|
||||||
link_name: String::from(link_name),
|
|
||||||
path: entry.to_owned()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
links.push(rst_file);
|
let file_name = match file_name.to_str() {
|
||||||
|
Some(name) => name,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(caps) = re.captures(file_name) {
|
||||||
|
let link_name = &caps["link_name"];
|
||||||
|
let rank = &caps["rank"];
|
||||||
|
|
||||||
|
let rank: u32 = if rank.is_empty() {
|
||||||
|
std::u32::MAX
|
||||||
|
} else {
|
||||||
|
match rank.parse() {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => std::u32::MAX
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let site_file = SiteFile {
|
||||||
|
rank,
|
||||||
|
file_name: file_name.to_string(),
|
||||||
|
link_name: link_name.to_string(),
|
||||||
|
path: entry.path(),
|
||||||
|
};
|
||||||
|
|
||||||
|
pages.push(site_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pages.sort_by(|a, b| a.rank.cmp(&b.rank));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -108,39 +146,62 @@ fn get_pages(path: &str, links: &mut Vec<SiteFile>) -> io::Result<()> {
|
|||||||
///
|
///
|
||||||
/// * `path` - path to search in
|
/// * `path` - path to search in
|
||||||
/// * `page_name` - file to look for
|
/// * `page_name` - file to look for
|
||||||
fn get_page(path: &str, page_name: &str) -> Result<SiteFile, PageNotFoundError> {
|
fn get_page(path: &Path) -> PageResult<SiteFile> {
|
||||||
let mut pages: Vec<SiteFile> = Vec::new();
|
let file_name = path.file_name().ok_or(PageNotFoundError)?;
|
||||||
|
let file_name = file_name.to_str().ok_or(PageNotFoundError)?.to_string();
|
||||||
|
if path.exists() {
|
||||||
|
return Ok(SiteFile {
|
||||||
|
rank: 0,
|
||||||
|
file_name: file_name.clone(),
|
||||||
|
link_name: file_name.clone(),
|
||||||
|
path: path.to_path_buf()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let mut dir_path = path.to_path_buf();
|
||||||
|
dir_path.pop();
|
||||||
|
|
||||||
// Get pages
|
for entry in dir_path.read_dir()? {
|
||||||
match get_pages(path, &mut pages) {
|
let entry = entry?;
|
||||||
Err(_) => return Err(PageNotFoundError),
|
let entry_name = entry.file_name().into_string().unwrap();
|
||||||
Ok(_) => (),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Look for the page in the directory
|
if entry_name.contains(&file_name) {
|
||||||
for page in pages {
|
return Ok(SiteFile {
|
||||||
if page.link_name.eq_ignore_ascii_case(page_name) {
|
rank: 0,
|
||||||
return Ok(page)
|
file_name: entry_name,
|
||||||
|
link_name: file_name,
|
||||||
|
path: entry.path()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(PageNotFoundError)
|
Err(JSiteError::from(PageNotFoundError))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_page(page: &str) -> Template {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert("error_page", page);
|
||||||
|
return Template::render("404", map);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a rendered template of a raw rst page if it exists
|
/// Returns a rendered template of a raw rst page if it exists
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `page` - a string containing the name of the rst file to look for
|
/// * `page` - path to page
|
||||||
#[get("/about/<page..>")]
|
#[get("/about/<page..>")]
|
||||||
fn rst_page(page: PathBuf) -> Template {
|
fn rst_page(page: PathBuf) -> Template {
|
||||||
|
let mut path = PathBuf::from("static/raw_rst");
|
||||||
|
path.push(page);
|
||||||
|
|
||||||
// Try and get the page
|
// 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()) {
|
let site_page = match get_page(
|
||||||
|
path.as_path()
|
||||||
|
) {
|
||||||
Ok(site_page) => site_page,
|
Ok(site_page) => site_page,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let mut map = HashMap::new();
|
return error_page(path.to_str().unwrap());
|
||||||
map.insert("error_page", page);
|
|
||||||
return Template::render("404", map)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -149,28 +210,27 @@ fn rst_page(page: PathBuf) -> Template {
|
|||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
let mut sub_files: Vec<SiteFile> = Vec::new();
|
let mut sub_files: Vec<SiteFile> = Vec::new();
|
||||||
match get_pages(site_page.path.to_str().unwrap(), &mut sub_files) {
|
match get_pages(site_page.path.to_str().unwrap(), &mut sub_files) {
|
||||||
Err(_) => (),
|
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
};
|
Err(_) => return error_page(&site_page.link_name)
|
||||||
|
}
|
||||||
|
|
||||||
let page_data = PageData {
|
let page_data = PageData {
|
||||||
links: sub_files,
|
links: sub_files,
|
||||||
site_file: site_page
|
site_file: site_page,
|
||||||
};
|
};
|
||||||
|
|
||||||
map.insert("page_data", page_data);
|
map.insert("page_data", page_data);
|
||||||
return Template::render("listing", &map);
|
return Template::render("listing", &map);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Else, render the RST page
|
// Else, render the RST page
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
let contents = match fs::read_to_string(site_page.path) {
|
let contents = match fs::read_to_string(site_page.path) {
|
||||||
Ok(contents) => contents,
|
Ok(contents) => contents,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("error_page", page);
|
map.insert("error_page", site_page.link_name);
|
||||||
return Template::render("404", map)
|
return Template::render("404", map);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render links
|
// Render links
|
||||||
@ -184,10 +244,8 @@ fn rst_page(page: PathBuf) -> Template {
|
|||||||
map.insert("content", contents);
|
map.insert("content", contents);
|
||||||
Template::render("rst_page", &map)
|
Template::render("rst_page", &map)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Catches 404 errors and displays an error message
|
/// Catches 404 errors and displays an error message
|
||||||
///
|
///
|
||||||
/// #Arguments
|
/// #Arguments
|
||||||
@ -202,11 +260,10 @@ fn not_found(req: &Request<'_>) -> Template {
|
|||||||
Template::render("404", &map)
|
Template::render("404", &map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Launches website
|
/// Launches website
|
||||||
fn rocket() -> rocket::Rocket {
|
fn rocket() -> rocket::Rocket {
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.mount("/", routes![index, rst_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])
|
||||||
|
@ -7,9 +7,9 @@ use std::collections::HashMap;
|
|||||||
///
|
///
|
||||||
/// * `string` - input RST string
|
/// * `string` - input RST string
|
||||||
///
|
///
|
||||||
pub fn parse_links(string: & String) -> String {
|
pub fn parse_links(string: &String) -> String {
|
||||||
let re_link_ref = Regex::new(r"\n?.. _(.*): (.*)\n").unwrap();
|
let re_link_ref = Regex::new(r"\n?.. _(.*): (.*)\n").unwrap();
|
||||||
let mut link_map: HashMap::<String, String> = HashMap::new();
|
let mut link_map: HashMap<String, String> = HashMap::new();
|
||||||
|
|
||||||
for cap in re_link_ref.captures_iter(string.as_str()) {
|
for cap in re_link_ref.captures_iter(string.as_str()) {
|
||||||
link_map.insert(String::from(&cap[1]), String::from(&cap[2]));
|
link_map.insert(String::from(&cap[1]), String::from(&cap[2]));
|
||||||
@ -21,12 +21,13 @@ pub fn parse_links(string: & String) -> String {
|
|||||||
for cap in re_link.captures_iter(output.clone().as_ref()) {
|
for cap in re_link.captures_iter(output.clone().as_ref()) {
|
||||||
let link = match link_map.get(&cap[1]) {
|
let link = match link_map.get(&cap[1]) {
|
||||||
None => String::from(""),
|
None => String::from(""),
|
||||||
Some(link) => link.to_owned()
|
Some(link) => link.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
output = output.replace(&cap[0],
|
output = output.replace(
|
||||||
format!("<a class=\"link\" href=\"{}\">{}</a>",
|
&cap[0],
|
||||||
link, &cap[1]).as_str());
|
format!("<a class=\"link\" href=\"{}\">{}</a>", link, &cap[1]).as_str(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
output
|
||||||
|
@ -3,9 +3,11 @@ use super::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_link_parser() {
|
fn test_link_parser() {
|
||||||
let mut input = String::from("This is a paragraph that contains `a link`_.
|
let mut input = String::from(
|
||||||
|
"This is a paragraph that contains `a link`_.
|
||||||
|
|
||||||
.. _a link: https://domain.invalida\n");
|
.. _a link: https://domain.invalida\n",
|
||||||
|
);
|
||||||
|
|
||||||
let output = parse_links(&mut input);
|
let output = parse_links(&mut input);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user