Updated error handling and made more idiomatic
+ Most unwraps removed + Better use of Rust constructs
This commit is contained in:
		
							parent
							
								
									e88d9d3179
								
							
						
					
					
						commit
						6733076068
					
				
							
								
								
									
										191
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,22 +1,26 @@ | ||||
| #![feature(proc_macro_hygiene, decl_macro)] | ||||
| 
 | ||||
| #[macro_use] extern crate rocket; | ||||
| #[macro_use] extern crate serde_derive; | ||||
| #[macro_use] | ||||
| extern crate rocket; | ||||
| #[macro_use] | ||||
| extern crate serde_derive; | ||||
| 
 | ||||
| mod tests; | ||||
| 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 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::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)] | ||||
| 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)] | ||||
| struct SiteFile { | ||||
|     rank: u32, | ||||
|     file_name: String, | ||||
|     link_name: String, | ||||
|     path: PathBuf, | ||||
| @ -55,7 +78,7 @@ fn index() -> Template { | ||||
| 
 | ||||
|     // Get the links to display on the main page
 | ||||
|     match get_pages("static/raw_rst", &mut links) { | ||||
|        Err(_) => (), | ||||
|         Err(_) => (), | ||||
|         Ok(_) => (), | ||||
|     } | ||||
| 
 | ||||
| @ -63,42 +86,57 @@ fn index() -> Template { | ||||
|     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
 | ||||
| ///
 | ||||
| /// * `links` - A reference to a vector of string to insert the links into
 | ||||
| fn get_pages(path: &str, links: &mut Vec<SiteFile>) -> io::Result<()> { | ||||
|     // Gather all of the rst files in static/raw_rst/
 | ||||
|     let mut entries: Vec<PathBuf> =  fs::read_dir(path)? | ||||
|         .map(|res| res.map(|e| e.path())) | ||||
|         .collect::<Result<Vec<_>, io::Error>>()?; | ||||
| 
 | ||||
|     // Sort so they are always in the same order
 | ||||
|     entries.sort(); | ||||
| /// * `path` - the path to look for pages in
 | ||||
| /// * `pages` - A vector where found pages will be inserted
 | ||||
| fn get_pages(path: &str, pages: &mut Vec<SiteFile>) -> io::Result<()> { | ||||
|     let re = Regex::new(r"(?P<rank>^\d*)(?P<link_name>.+)").unwrap(); | ||||
| 
 | ||||
|     // Find all files in the directory
 | ||||
|     for entry in entries { | ||||
|         let file_name = entry.file_stem().unwrap().to_str().unwrap(); | ||||
|         let link_name; | ||||
|         if file_name.chars().next().unwrap().is_numeric() { | ||||
|             link_name = &file_name[1..]; | ||||
|         } | ||||
|         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() | ||||
|     for entry in fs::read_dir(path)? { | ||||
|         let entry = entry?; | ||||
|         let path = entry.path(); | ||||
|         let file_name = match path.file_stem() { | ||||
|             Some(name) => name, | ||||
|             None => continue, | ||||
|         }; | ||||
| 
 | ||||
|         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(()) | ||||
| } | ||||
| 
 | ||||
| @ -108,39 +146,62 @@ fn get_pages(path: &str, links: &mut Vec<SiteFile>) -> io::Result<()> { | ||||
| ///
 | ||||
| /// * `path` - path to search in
 | ||||
| /// * `page_name` - file to look for
 | ||||
| fn get_page(path: &str, page_name: &str) -> Result<SiteFile, PageNotFoundError> { | ||||
|     let mut pages: Vec<SiteFile> = Vec::new(); | ||||
| fn get_page(path: &Path) -> PageResult<SiteFile> { | ||||
|     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
 | ||||
|     match get_pages(path, &mut pages) { | ||||
|         Err(_) => return Err(PageNotFoundError), | ||||
|         Ok(_) => (), | ||||
|     }; | ||||
|         for entry in dir_path.read_dir()? { | ||||
|             let entry = entry?; | ||||
|             let entry_name = entry.file_name().into_string().unwrap(); | ||||
| 
 | ||||
|     // Look for the page in the directory
 | ||||
|     for page in pages { | ||||
|         if page.link_name.eq_ignore_ascii_case(page_name) { | ||||
|             return Ok(page) | ||||
|             if entry_name.contains(&file_name) { | ||||
|                 return Ok(SiteFile { | ||||
|                     rank: 0, | ||||
|                     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
 | ||||
| ///
 | ||||
| /// # Arguments
 | ||||
| ///
 | ||||
| /// * `page` - a string containing the name of the rst file to look for
 | ||||
| /// * `page` - path to page
 | ||||
| #[get("/about/<page..>")] | ||||
| fn rst_page(page: PathBuf) -> Template { | ||||
|     let mut path = PathBuf::from("static/raw_rst"); | ||||
|     path.push(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, | ||||
|         Err(_) => { | ||||
|             let mut map = HashMap::new(); | ||||
|             map.insert("error_page", page); | ||||
|             return Template::render("404", map) | ||||
|             return error_page(path.to_str().unwrap()); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| @ -149,28 +210,27 @@ fn rst_page(page: PathBuf) -> Template { | ||||
|         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(_) => (), | ||||
|         }; | ||||
|             Err(_) => return error_page(&site_page.link_name) | ||||
|         } | ||||
| 
 | ||||
|         let page_data = PageData { | ||||
|             links: sub_files, | ||||
|             site_file: site_page | ||||
|             site_file: site_page, | ||||
|         }; | ||||
| 
 | ||||
|         map.insert("page_data", page_data); | ||||
|         return Template::render("listing", &map); | ||||
|     } | ||||
|     else { | ||||
|     } else { | ||||
|         // Else, render the RST page
 | ||||
|         let mut map = HashMap::new(); | ||||
|         let 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) | ||||
|             }, | ||||
|                 map.insert("error_page", site_page.link_name); | ||||
|                 return Template::render("404", map); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         // Render links
 | ||||
| @ -184,10 +244,8 @@ fn rst_page(page: PathBuf) -> Template { | ||||
|         map.insert("content", contents); | ||||
|         Template::render("rst_page", &map) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /// Catches 404 errors and displays an error message
 | ||||
| ///
 | ||||
| /// #Arguments
 | ||||
| @ -202,11 +260,10 @@ fn not_found(req: &Request<'_>) -> Template { | ||||
|     Template::render("404", &map) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /// Launches website
 | ||||
| fn rocket() -> rocket::Rocket { | ||||
|     rocket::ignite() | ||||
|         .mount("/", routes![index, rst_page], ) | ||||
|         .mount("/", routes![index, rst_page]) | ||||
|         .mount("/static", StaticFiles::from("static")) | ||||
|         .attach(Template::fairing()) | ||||
|         .register(catchers![not_found]) | ||||
| @ -215,4 +272,4 @@ fn rocket() -> rocket::Rocket { | ||||
| /// Main
 | ||||
| fn main() { | ||||
|     rocket().launch(); | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -7,9 +7,9 @@ use std::collections::HashMap; | ||||
| ///
 | ||||
| /// * `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 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()) { | ||||
|         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()) { | ||||
|         let link = match link_map.get(&cap[1]) { | ||||
|             None => String::from(""), | ||||
|             Some(link) => link.to_owned() | ||||
|             Some(link) => link.to_owned(), | ||||
|         }; | ||||
| 
 | ||||
|         output = output.replace(&cap[0], | ||||
|                                 format!("<a class=\"link\" href=\"{}\">{}</a>", | ||||
|                                         link, &cap[1]).as_str()); | ||||
|         output = output.replace( | ||||
|             &cap[0], | ||||
|             format!("<a class=\"link\" href=\"{}\">{}</a>", link, &cap[1]).as_str(), | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     output | ||||
|  | ||||
| @ -3,9 +3,11 @@ use super::*; | ||||
| 
 | ||||
| #[test] | ||||
| 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); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user