1#![deny(clippy::pedantic)]
2use set_error::ChangeError;
5
6use std::{
7 path::Path,
8 rc::Rc,
9 thread,
10 time::{Duration, SystemTime},
11};
12
13#[derive(Clone)]
14pub struct FileListBuilder<T: Clone> {
15 files: Vec<WatchedFile<T>>,
16 interval: Duration,
17 max_retries: Option<u32>,
18 open_file_func: Rc<Fn(&str) -> WatchingFuncResult<T>>,
19 run_only_once: bool,
20}
21
22#[derive(Clone)]
23pub struct WatchedFile<T> {
24 path: String,
25 date_modified: SystemTime,
26 functions_on_run: Vec<Rc<Fn(T) -> WatchingFuncResult<T>>>,
27 function_on_end: Rc<Fn(T) -> Result<(), String>>,
28}
29
30pub enum WatchingFuncResult<T> {
31 Success(T),
32 Retry(String),
33 Fail(String),
34}
35use WatchingFuncResult::{Fail, Retry, Success};
36
37impl<T: Clone> FileListBuilder<T> {
38 pub fn new<F: 'static + Fn(&str) -> WatchingFuncResult<T>>(open_func: F) -> Self {
39 Self {
40 files: Vec::new(),
41 interval: Duration::from_millis(1000),
42 max_retries: None,
43 open_file_func: Rc::new(open_func),
44 run_only_once: false,
45 }
46 }
47 pub fn run_only_once(mut self, q: bool) -> Self {
48 self.run_only_once = q;
49 self
50 }
51 pub fn add_file(&mut self, file: WatchedFile<T>) {
52 self.files.push(file);
53 }
54 pub fn with_interval(mut self, inter: Duration) -> Self {
55 self.interval = inter;
56 self
57 }
58 pub fn with_max_retries(mut self, re: u32) -> Self {
59 self.max_retries = Some(re);
60 self
61 }
62 pub fn launch(mut self) -> Result<(), String> {
63 let mut on_first_run = self.files.len() + 1;
64 loop {
65 for mut file in &mut self.files {
66 if on_first_run != 0 {
67 on_first_run -= 1
68 }
69 if (on_first_run != 0) || (file.date_modified != date_modified(&file.path)?) {
70 file.date_modified = date_modified(&file.path)?;
71 let open_file_func_as_not_mut = self.open_file_func.clone();
72 let mut file_data = keep_doing_until(self.max_retries, self.interval, || {
73 (open_file_func_as_not_mut)(&file.path)
74 })?;
75 for function_to_run in file.functions_on_run.clone() {
76 file_data = keep_doing_until(self.max_retries, self.interval, || {
77 function_to_run(file_data.clone())
78 })?
79 }
80 let mut retries = self.max_retries;
81 loop {
82 match (file.function_on_end)(file_data.clone()) {
83 Ok(_) => break,
84 Err(s) => {
85 retries = retries.map(|x| x - 1);
86 match retries {
87 Some(n) if n == 0 => {
88 return Err(String::from("no more retries"))
89 }
90 _ => {
91 println!("{}", s);
92 thread::sleep(self.interval);
93 continue;
94 }
95 }
96 }
97 }
98 }
99 thread::sleep(self.interval);
100 }
101 }
102 if self.run_only_once {
103 return Ok(());
104 }
105 }
106 }
107}
108
109fn keep_doing_until<F, T>(mut retries: Option<u32>, interval: Duration, f: F) -> Result<T, String>
110where
111 F: Fn() -> WatchingFuncResult<T>,
112{
113 Ok(loop {
114 match f() {
115 Success(t) => break t,
116 Fail(s) => return Err(s),
117 Retry(s) => {
118 retries = retries.map(|x| x - 1);
119 match retries {
120 Some(n) if n == 0 => return Err(String::from("no more retries")),
121 _ => {
122 println!("{}", s);
123 thread::sleep(interval);
124 continue;
125 }
126 }
127 }
128 }
129 })
130}
131
132impl<T> WatchedFile<T> {
133 pub fn new<G: 'static + Fn(T) -> Result<(), String>>(
134 path: &str,
135 end_func: G,
136 ) -> Result<Self, String> {
137 Ok(Self {
138 path: path.to_string(),
139 date_modified: date_modified(&path)?,
140 functions_on_run: Vec::new(),
141 function_on_end: Rc::new(end_func),
142 })
143 }
144 pub fn add_func<F: 'static + Fn(T) -> WatchingFuncResult<T>>(&mut self, func: F) {
145 self.functions_on_run.push(Rc::new(func));
146 }
147}
148
149fn date_modified(path: &str) -> Result<SystemTime, String> {
150 Ok(Path::new(path)
151 .metadata()
152 .set_error(&format!("failed to open file {} metadata", path))?
153 .modified()
154 .set_error(&format!("failed to find files date modified {}", path))?)
155}