Added RST image parsing
+ Added DIP project
This commit is contained in:
		
							parent
							
								
									9fbad90eb8
								
							
						
					
					
						commit
						68ecb118a9
					
				
							
								
								
									
										31
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -5,9 +5,10 @@ extern crate rocket; | |||||||
| #[macro_use] | #[macro_use] | ||||||
| extern crate serde_derive; | extern crate serde_derive; | ||||||
| 
 | 
 | ||||||
| mod rst_parser; |  | ||||||
| mod tests; | mod tests; | ||||||
|  | mod rst_parser; | ||||||
| 
 | 
 | ||||||
|  | use crate::rst_parser::parse_images; | ||||||
| use regex::Regex; | use regex::Regex; | ||||||
| use rocket::Request; | use rocket::Request; | ||||||
| use rocket_contrib::serve::StaticFiles; | use rocket_contrib::serve::StaticFiles; | ||||||
| @ -16,9 +17,9 @@ use rst_parser::parse_links; | |||||||
| use std::collections::HashMap; | use std::collections::HashMap; | ||||||
| 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; | use std::io::Error; | ||||||
|  | use std::path::{Path, PathBuf}; | ||||||
|  | use std::{fs, io}; | ||||||
| 
 | 
 | ||||||
| type PageResult<T> = std::result::Result<T, JSiteError>; | type PageResult<T> = std::result::Result<T, JSiteError>; | ||||||
| 
 | 
 | ||||||
| @ -117,7 +118,7 @@ fn get_pages(path: &str, pages: &mut Vec<SiteFile>) -> io::Result<()> { | |||||||
|             } else { |             } else { | ||||||
|                 match rank.parse() { |                 match rank.parse() { | ||||||
|                     Ok(r) => r, |                     Ok(r) => r, | ||||||
|                     Err(_) => std::u32::MAX |                     Err(_) => std::u32::MAX, | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
| @ -144,16 +145,15 @@ fn get_pages(path: &str, pages: &mut Vec<SiteFile>) -> io::Result<()> { | |||||||
| /// * `page_name` - file to look for
 | /// * `page_name` - file to look for
 | ||||||
| fn get_page(path: &Path) -> PageResult<SiteFile> { | fn get_page(path: &Path) -> PageResult<SiteFile> { | ||||||
|     let file_name = path.file_name().ok_or(PageNotFoundError)?; |     let file_name = path.file_name().ok_or(PageNotFoundError)?; | ||||||
|     let file_name =  file_name.to_str().ok_or(PageNotFoundError)?.to_string(); |     let file_name = file_name.to_str().ok_or(PageNotFoundError)?.to_string(); | ||||||
|     if path.exists() { |     if path.exists() { | ||||||
|         return Ok(SiteFile { |         return Ok(SiteFile { | ||||||
|             rank: 0, |             rank: 0, | ||||||
|             file_name: file_name.clone(), |             file_name: file_name.clone(), | ||||||
|             link_name: file_name, |             link_name: file_name, | ||||||
|             path: path.to_path_buf() |             path: path.to_path_buf(), | ||||||
|         }) |         }); | ||||||
|     } |     } else { | ||||||
|     else { |  | ||||||
|         let mut dir_path = path.to_path_buf(); |         let mut dir_path = path.to_path_buf(); | ||||||
|         dir_path.pop(); |         dir_path.pop(); | ||||||
| 
 | 
 | ||||||
| @ -166,8 +166,8 @@ fn get_page(path: &Path) -> PageResult<SiteFile> { | |||||||
|                     rank: 0, |                     rank: 0, | ||||||
|                     file_name: entry_name, |                     file_name: entry_name, | ||||||
|                     link_name: file_name, |                     link_name: file_name, | ||||||
|                     path: entry.path() |                     path: entry.path(), | ||||||
|                 }) |                 }); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -191,9 +191,7 @@ fn rst_page(page: PathBuf) -> Template { | |||||||
|     path.push(page); |     path.push(page); | ||||||
| 
 | 
 | ||||||
|     // Try and get the page
 |     // Try and get the page
 | ||||||
|     let site_page = match get_page( |     let site_page = match get_page(path.as_path()) { | ||||||
|         path.as_path() |  | ||||||
|     ) { |  | ||||||
|         Ok(site_page) => site_page, |         Ok(site_page) => site_page, | ||||||
|         Err(_) => { |         Err(_) => { | ||||||
|             return error_page(path.to_str().unwrap()); |             return error_page(path.to_str().unwrap()); | ||||||
| @ -206,7 +204,7 @@ fn rst_page(page: PathBuf) -> Template { | |||||||
|         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) { | ||||||
|             Ok(_) => (), |             Ok(_) => (), | ||||||
|             Err(_) => return error_page(&site_page.link_name) |             Err(_) => return error_page(&site_page.link_name), | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let page_data = PageData { |         let page_data = PageData { | ||||||
| @ -229,7 +227,8 @@ fn rst_page(page: PathBuf) -> Template { | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         // Render links
 |         // Render links
 | ||||||
|         let mut contents = parse_links(&contents); |         let mut contents = parse_links(&contents).unwrap(); | ||||||
|  |         contents = parse_images(contents.as_str()).unwrap(); | ||||||
| 
 | 
 | ||||||
|         // Ensure render will look good
 |         // Ensure render will look good
 | ||||||
|         contents = contents.replace("\n", "<br>"); |         contents = contents.replace("\n", "<br>"); | ||||||
|  | |||||||
| @ -1,32 +0,0 @@ | |||||||
| use regex::{Regex, Captures}; |  | ||||||
| use std::collections::HashMap; |  | ||||||
| 
 |  | ||||||
| /// Renders RST links as HTML links
 |  | ||||||
| ///
 |  | ||||||
| /// # Arguments
 |  | ||||||
| ///
 |  | ||||||
| /// * `string` - input RST string
 |  | ||||||
| ///
 |  | ||||||
| pub fn parse_links(string: &str) -> String { |  | ||||||
|     let re_link_ref = Regex::new(r"\n?.. _(.*): (.*)\n").unwrap(); |  | ||||||
|     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"`(.*)`_").unwrap(); |  | ||||||
| 
 |  | ||||||
|     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]) |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     output.to_string() |  | ||||||
| } |  | ||||||
							
								
								
									
										111
									
								
								src/rst_parser/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/rst_parser/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,111 @@ | |||||||
|  | pub mod rst_error; | ||||||
|  | 
 | ||||||
|  | use regex::{Captures, Regex}; | ||||||
|  | use std::collections::HashMap; | ||||||
|  | use rst_error::RSTError; | ||||||
|  | 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!("{} {}", 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()) | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								src/rst_parser/rst_error.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/rst_parser/rst_error.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | 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), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								static/raw_rst/projects/motion_detector.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								static/raw_rst/projects/motion_detector.rst
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | 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 | ||||||
|  | 
 | ||||||
| @ -42,5 +42,6 @@ improving how the site looked on mobile. | |||||||
| 
 | 
 | ||||||
| Future Improvements | Future Improvements | ||||||
| ------------------- | ------------------- | ||||||
| As I continue to learn Rust, I plan to implement more features of RST into the raw file displays. For example, | As I continue to learn Rust, I plan to implement more features of RST into the raw file displays. ~~For example, | ||||||
| embedding images. I want to strike a balance between the aesthetic of a terminal and ease of use of a website. | embedding images~~ (now onto text formatting...). I want to strike a balance between the aesthetic of a terminal and | ||||||
|  | ease of use of a website. | ||||||
|  | |||||||
| @ -5,8 +5,8 @@ ls {{ page_data.site_file.link_name }} | |||||||
| {% endblock command %} | {% endblock command %} | ||||||
| 
 | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
| {% for link in page_data.links %} |  | ||||||
| <br> | <br> | ||||||
|  | {% for link in page_data.links %} | ||||||
| <a class="link" href="{{ page_data.site_file.link_name}}/{{ link.link_name }}">{{ link.link_name }}</a>   | <a class="link" href="{{ page_data.site_file.link_name}}/{{ link.link_name }}">{{ link.link_name }}</a>   | ||||||
| {% endfor %} | {% endfor %} | ||||||
| <br> | <br> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user