Split Job struct into it's own module
This commit is contained in:
		
							parent
							
								
									514209dae2
								
							
						
					
					
						commit
						38b5d5bb32
					
				|  | @ -0,0 +1,187 @@ | |||
| use std::path::{Path,PathBuf}; | ||||
| use std::str::FromStr; | ||||
| use std::time::Duration; | ||||
| use std::time::Instant; | ||||
| 
 | ||||
| use ansi_to_html; | ||||
| use chrono; | ||||
| use rss; | ||||
| 
 | ||||
| use crate::conf::Conf; | ||||
| 
 | ||||
| pub struct Job { | ||||
|     pub url: String, | ||||
|     pub selector: String, | ||||
|     pub every: Duration, | ||||
|     pub last_run: Option<Instant>, | ||||
|     pub output_file: Option<PathBuf>, | ||||
|     pub channel: Option<rss::Channel>, | ||||
| } | ||||
| 
 | ||||
| impl Job { | ||||
| 
 | ||||
|     fn default(conf: &Conf) -> Job { | ||||
|         return Job { | ||||
|             url: "".to_string(), | ||||
|             selector: "".to_string(), | ||||
|             every: conf.check_interval, | ||||
|             last_run: None, | ||||
|             output_file: None, | ||||
|             channel: None, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     pub fn new(url: &str, selector: &str, conf: &Conf) -> Job { | ||||
|         let mut job = Job::default(conf); | ||||
|         job.url = url.to_string(); | ||||
|         job.selector = selector.to_string(); | ||||
|         let mut output_file = conf.output_dir.clone(); | ||||
|         let mut file_name = job.url.clone().replace("/", "-"); | ||||
|         file_name.push_str(".rss"); | ||||
|         output_file = output_file.join(Path::new(&file_name)); | ||||
|         job.output_file = Some(output_file); | ||||
| 
 | ||||
|         match std::fs::File::open(job.output_file.as_ref().unwrap()) { | ||||
|             Err(why) => { | ||||
|                 println!("Failed to open '{}': {}", job.output_file.as_ref().unwrap().display(), why); | ||||
|                 println!("Creating empty RSS channel for job '{}'", job.url); | ||||
|                 job.channel = Some( | ||||
|                     rss::ChannelBuilder::default() | ||||
|                         .title(url) | ||||
|                         .link(url) | ||||
|                         .description("haunting") | ||||
|                         .build() | ||||
|                 ); | ||||
|                 job.channel.as_mut().unwrap().set_generator("Haunter".to_string()); | ||||
|             }, | ||||
|             Ok(file) => { | ||||
|                 job.channel = Some( | ||||
|                     rss::Channel::read_from(std::io::BufReader::new(file)).unwrap() | ||||
|                 ); | ||||
|             }, | ||||
|         }; | ||||
|         return job; | ||||
|     } | ||||
| 
 | ||||
|     pub fn from_file<'a>(path: &'a Path, conf: &'a Conf) -> Result<Job, &'a str> { | ||||
|         let mut job = Job::default(conf); | ||||
|         let items = match crate::conf::read_conf_file(path) { | ||||
|             Err(_) => return Err("Failed to read from configuration file"), | ||||
|             Ok(items) => items, | ||||
|         }; | ||||
|         for item in items.iter() { | ||||
|             let key = item.0.as_str(); | ||||
|             match key { | ||||
|                 "url" => { | ||||
|                     job.url = item.1.clone(); | ||||
|                 }, | ||||
|                 "selector" => { | ||||
|                     job.selector = item.1.clone(); | ||||
|                 }, | ||||
|                 "every" => { | ||||
|                     let converted_value = match item.1.parse::<u64>() { | ||||
|                         Err(why) => { | ||||
|                             println!("Failed to convert '{}' to u64: {}", item.1, why); | ||||
|                             return Err("Failed to parse value of 'every'"); | ||||
|                         }, | ||||
|                         Ok(v) => v, | ||||
|                     }; | ||||
|                     job.every = Duration::new(converted_value, 0); | ||||
|                 }, | ||||
|                 "output_file" => { | ||||
|                     job.output_file = Some( | ||||
|                         conf.output_dir.join(PathBuf::from_str(item.1.as_str()).unwrap()) | ||||
|                     ); | ||||
|                 } | ||||
|                 _ => { | ||||
|                     println!("Unknown key '{}' in job file '{}'", key, path.display()); | ||||
|                     return Err("Unknown key"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return Ok(job); | ||||
|     } | ||||
| 
 | ||||
|     pub fn update(&mut self, value: &str, diff: &str) { | ||||
|         if self.channel.is_none() { | ||||
|             println!("Skipping update of channel: no channel set"); | ||||
|             return; | ||||
|         } | ||||
|         let channel = self.channel.as_mut().unwrap(); | ||||
|         let update_time = chrono::Utc::now(); | ||||
|         let item = rss::ItemBuilder::default() | ||||
|             .title(format!("Update to '{}'", self.url)) | ||||
|             .link(self.url.clone()) | ||||
|             .pub_date(update_time.to_rfc2822()) | ||||
|             .content(format!(r#" | ||||
| New content at {}: <br> | ||||
| <pre class="new-value"> | ||||
| {} | ||||
| </pre> | ||||
| <br><br> | ||||
| Diff: <br> | ||||
| <pre class="diff-value"> | ||||
| {} | ||||
| </pre> | ||||
| "#,
 | ||||
|                              update_time.format("%d/%m/%Y %H:%M"), | ||||
|                              ansi_to_html::convert_escaped(value).unwrap().as_str(), | ||||
|                              ansi_to_html::convert_escaped(diff).unwrap().as_str() | ||||
|                 ) | ||||
|             ) | ||||
|             .build(); | ||||
| 
 | ||||
|         channel.items.push(item); | ||||
| 
 | ||||
|         if self.output_file.is_some() { | ||||
|             match std::fs::File::create(self.output_file.as_ref().unwrap()) { | ||||
|                 Err(why) => { | ||||
|                     println!("Failed to open '{}' for writing: {}", self.output_file.as_ref().unwrap().display(), why); | ||||
|                 }, | ||||
|                 Ok(file) => { | ||||
|                     channel.write_to( | ||||
|                         std::io::BufWriter::new(file) | ||||
|                     ).expect("Failed to write updated channel"); | ||||
|                 }, | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     use std::io::Write; | ||||
|     use tempfile::NamedTempFile; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn create() { | ||||
|         let conf = Conf::get_default_conf(); | ||||
|         let job = Job::new(&"my/url", &"myselector", &conf); | ||||
|         assert_eq!(job.url, "my/url"); | ||||
|         assert_eq!(job.output_file.unwrap().to_str().unwrap(), "results.d/my-url.rss"); | ||||
|         assert_eq!(job.selector, "myselector"); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn create_from_file() { | ||||
|         let conf = Conf::get_default_conf(); | ||||
|         let mut tf = NamedTempFile::new().unwrap(); | ||||
|         let job_conf = r#" | ||||
| url = http://example.com/test
 | ||||
| output_file = example_output.atom | ||||
| every=7200 | ||||
| 
 | ||||
| selector = section.listing:nth-child(2) > ul:nth-child(1) > li:nth-child(3) > header:nth-child(3) > h2:nth-child(1) > a:nth-child(1) | ||||
| "#;
 | ||||
|         tf.write_all(job_conf.as_bytes()).expect("Failed to write configuration to file"); | ||||
| 
 | ||||
|         let job = Job::from_file(tf.path(), &conf).expect("Failed to read configuration file"); | ||||
|         assert_eq!(job.url, "http://example.com/test"); | ||||
|         assert_eq!(job.output_file.unwrap().to_str().unwrap(), "results.d/example_output.atom"); | ||||
|         assert_eq!(job.every.as_secs(), 7200); | ||||
|         assert_eq!(job.selector, "section.listing:nth-child(2) > ul:nth-child(1) > li:nth-child(3) > header:nth-child(3) > h2:nth-child(1) > a:nth-child(1)"); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										101
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										101
									
								
								src/main.rs
								
								
								
								
							|  | @ -1,111 +1,17 @@ | |||
| use std::cmp::Ordering; | ||||
| use std::env; | ||||
| use std::path::{Path,PathBuf}; | ||||
| use std::str::FromStr; | ||||
| use std::path::Path; | ||||
| use std::thread; | ||||
| use std::time::Duration; | ||||
| use std::time::Instant; | ||||
| 
 | ||||
| use ansi_to_html; | ||||
| use chrono; | ||||
| use rss; | ||||
| use thirtyfour_sync::WebDriverCommands; | ||||
| 
 | ||||
| mod conf; | ||||
| use conf::Conf; | ||||
| 
 | ||||
| struct Job { | ||||
|     url: String, | ||||
|     selector: String, | ||||
|     every: Duration, | ||||
|     last_run: Option<Instant>, | ||||
|     output_file: Option<PathBuf>, | ||||
|     channel: Option<rss::Channel>, | ||||
| } | ||||
| 
 | ||||
| impl Job { | ||||
|     fn new(url: &str, selector: &str, conf: &Conf) -> Job { | ||||
|         let mut job = Job { | ||||
|             url: String::from_str(url).unwrap(), | ||||
|             selector: String::from_str(selector).unwrap(), | ||||
|             every: conf.check_interval, | ||||
|             last_run: None, | ||||
|             output_file: None, | ||||
|             channel: None, | ||||
|         }; | ||||
|         let mut output_file = conf.output_dir.clone(); | ||||
|         let mut file_name = job.url.clone().replace("/", "-"); | ||||
|         file_name.push_str(".rss"); | ||||
|         output_file = output_file.join(Path::new(&file_name)); | ||||
|         job.output_file = Some(output_file); | ||||
| 
 | ||||
|         match std::fs::File::open(job.output_file.as_ref().unwrap()) { | ||||
|             Err(why) => { | ||||
|                 println!("Failed to open '{}': {}", job.output_file.as_ref().unwrap().display(), why); | ||||
|                 println!("Creating empty RSS channel for job '{}'", job.url); | ||||
|                 job.channel = Some( | ||||
|                     rss::ChannelBuilder::default() | ||||
|                         .title(url) | ||||
|                         .link(url) | ||||
|                         .description("haunting") | ||||
|                         .build() | ||||
|                 ); | ||||
|                 job.channel.as_mut().unwrap().set_generator("Haunter".to_string()); | ||||
|             }, | ||||
|             Ok(file) => { | ||||
|                 job.channel = Some( | ||||
|                     rss::Channel::read_from(std::io::BufReader::new(file)).unwrap() | ||||
|                 ); | ||||
|             }, | ||||
|         }; | ||||
|         return job; | ||||
|     } | ||||
| 
 | ||||
|     fn update(&mut self, value: &str, diff: &str) { | ||||
|         if self.channel.is_none() { | ||||
|             println!("Skipping update of channel: no channel set"); | ||||
|             return; | ||||
|         } | ||||
|         let channel = self.channel.as_mut().unwrap(); | ||||
|         let update_time = chrono::Utc::now(); | ||||
|         let item = rss::ItemBuilder::default() | ||||
|             .title(format!("Update to '{}'", self.url)) | ||||
|             .link(self.url.clone()) | ||||
|             .pub_date(update_time.to_rfc2822()) | ||||
|             .content(format!(r#" | ||||
| New content at {}: <br> | ||||
| <pre class="new-value"> | ||||
| {} | ||||
| </pre> | ||||
| <br><br> | ||||
| Diff: <br> | ||||
| <pre class="diff-value"> | ||||
| {} | ||||
| </pre> | ||||
| "#,
 | ||||
|                              update_time.format("%d/%m/%Y %H:%M"), | ||||
|                              ansi_to_html::convert_escaped(value).unwrap().as_str(), | ||||
|                              ansi_to_html::convert_escaped(diff).unwrap().as_str() | ||||
|                 ) | ||||
|             ) | ||||
|             .build(); | ||||
| 
 | ||||
|         channel.items.push(item); | ||||
| 
 | ||||
|         if self.output_file.is_some() { | ||||
|             match std::fs::File::create(self.output_file.as_ref().unwrap()) { | ||||
|                 Err(why) => { | ||||
|                     println!("Failed to open '{}' for writing: {}", self.output_file.as_ref().unwrap().display(), why); | ||||
|                 }, | ||||
|                 Ok(file) => { | ||||
|                     channel.write_to( | ||||
|                         std::io::BufWriter::new(file) | ||||
|                     ).expect("Failed to write updated channel"); | ||||
|                 }, | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| mod job; | ||||
| use job::Job; | ||||
| 
 | ||||
| struct ThreadJob { | ||||
|     job: Job, | ||||
|  | @ -265,6 +171,7 @@ fn main() { | |||
|             } | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         std::thread::sleep(Duration::new(1, 0)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue