1use log::{debug, info};
76use std::fmt;
77use std::io::Write;
78use std::panic;
79use std::panic::PanicInfo;
80use yaml_rust::{Yaml, YamlLoader};
81
82type StrList = [&'static [&'static str]];
83type Panicfn = Box<dyn Fn(&PanicInfo) + Sync + Send>;
84
85#[derive(Debug, Clone)]
86pub struct UserPanic {
88 pub error_msg: &'static str,
92 pub fix_instructions: Option<&'static StrList>,
94}
95impl fmt::Display for UserPanic {
96 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97 if self.error_msg == "" {
98 return write!(f, "");
99 }
100 let mut s = String::from("The Program Crashed\n\n");
102 if self.fix_instructions.is_none() {
103 s += &format!("Error: {}", self.error_msg);
104 s += "\nIt seems like an error that can't be fixed by you!\nPlease submit a Bug report to Developer\n";
105 } else {
106 s += &format!("Error: {}", self.error_msg);
107 s += "\nIt seems like an error that can be fixed by you!\nPlease follow the following instructions to try and fix the Error\n";
108 let insts = self.fix_instructions.as_ref().unwrap();
109 let mut i = 1;
110 for inst in *insts {
111 s += &format!("\n\t{}: {}\n", i, inst[0]);
112 let inst = &inst[1..];
113 if inst.len() > 1 {
114 let mut j = 1;
115 for ii in inst {
116 s += &format!("\t\t{}. {}\n", j, ii);
117 j += 1;
118 }
119 }
120 i += 1;
121 }
122 }
123 write!(f, "{}", s)
124 }
125}
126pub fn set_hooks(developer: Option<&'static str>) {
129 let org: Panicfn = panic::take_hook();
130 if let Some(dev) = developer {
131 panic::set_hook(Box::new(move |pan_inf| {
133 panic_func(pan_inf, &org);
134 eprintln!("{}", dev);
135 }))
136 } else {
137 panic::set_hook(Box::new(move |pan_inf| {
139 panic_func(pan_inf, &org);
140 }));
141 }
142}
143fn panic_func(panic_info: &PanicInfo, original: &Panicfn) {
145 match panic_info.payload().downcast_ref::<UserPanic>() {
146 Some(err) => {
147 if err.error_msg != "" {
148 eprintln!("{}", err);
149 }
150 }
151 None => original(panic_info),
153 }
154}
155fn read_from_yml(yaml: String) -> String {
157 debug!("Started Reading the yaml string");
158 let mut file = "use user_panic::UserPanic;\n".to_string();
159 let yaml = YamlLoader::load_from_str(&yaml).unwrap();
160 let structs = &yaml[0];
161 if let Yaml::Hash(hash) = structs {
162 info!("Found Hash");
163 for (key, val) in hash {
165 let st_name = key.as_str().unwrap();
166 debug!("parsing key {}", st_name);
167 file += &format!(
168 "pub const {}:UserPanic = UserPanic {{{}}};",
169 st_name,
170 get_err_msg(val)
171 );
172 }
173 }
174 file
175}
176fn get_err_msg(hash: &Yaml) -> String {
179 let print_arr = |arr: &Vec<Yaml>| -> String {
180 let mut s = String::new();
181 let _ = arr
182 .iter()
183 .map(|a| {
184 s += &format!(",\"{}\"", a.as_str().unwrap());
185 })
186 .collect::<Vec<_>>();
187 s
188 };
189 let mut s = String::new();
190 debug!("found hash {:#?}", hash);
191 let err_ms = hash["message"].as_str().unwrap();
192 debug!("Collecting err message: {}", err_ms);
193 debug!("{:?}", &hash["fix instructions"]);
194 if let Yaml::Array(arr) = &hash["fix instructions"] {
195 debug!("Found fix instructions");
196 s += &format!("error_msg:\"{}\",fix_instructions:Some(&[", err_ms);
197 let items = arr.len();
198 debug!("Number of instuctions {}", items);
199 let mut i = 0;
200 while i < items {
201 if i + 1 < items {
202 match &arr[i + 1] {
203 Yaml::String(_) => {
204 s += &format!("&[\"{}\"],", arr[i].as_str().unwrap());
205 i += 1;
206 }
207 Yaml::Array(ar) => {
208 s += &format!("&[\"{}\"{}],", arr[i].as_str().unwrap(), print_arr(ar));
209 i += 2;
210 }
211 _ => {}
212 }
213 } else {
214 match &arr[i] {
215 Yaml::String(ss) => {
216 s += &format!("&[\"{}\"],", ss);
217 i += 1;
218 }
219 Yaml::Array(ar) => {
220 s += &format!("&[\"{}\"{}],", arr[i].as_str().unwrap(), print_arr(ar));
221 i += 2;
222 }
223 _ => {}
224 }
225 }
226 }
227 s += "]),";
228 } else {
229 s += &format!("error_msg:\"{}\",fix_instructions: None,", err_ms);
230 }
231 s
232}
233
234#[macro_export]
235macro_rules! panic_setup {
238 ($file_path:expr) => {
239 user_panic::panic_setup_function($file_path, "src/panic_structs.rs");
240 };
241 ($file_path:expr,$file_out:expr) => {
242 user_panic::panic_setup_function($file_path, $file_out);
243 };
244}
245pub fn panic_setup_function(path_from: &str, path_to: &str) {
248 let file_str = std::fs::read_to_string(path_from).expect("Failed to read yaml file");
249 let s = read_from_yml(file_str);
250 let mut fp = std::fs::File::create(path_to).expect("failed to create output file");
251 write!(&mut fp, "{}", s).expect("failed to write to file");
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 #[test]
258 #[should_panic]
259 fn it_works() {
260 const ERROR: UserPanic = UserPanic {
261 error_msg: "This is an error",
262 fix_instructions: Some(&[
263 &["Only one"],
264 &["one", "two", "tem"],
265 &["bem", "lem", "jem"],
266 ]),
267 };
268
269 set_hooks(None);
270 std::panic::panic_any(ERROR);
271 }
272
273 #[test]
274 fn print_s() {
275 let s = "
277foo:
278 message: this is the main error
279 fix instructions:
280 - first
281 - - in first
282 - in first second
283 - second
284 - - second first
285 - second second
286 - third
287bar:
288 message: This is un fixable error
289";
290 let s = read_from_yml(s.to_string());
291 assert_eq!("use user_panic::UserPanic;\npub const foo:UserPanic = UserPanic {error_msg:\"this is the main error\",fix_instructions:Some(&[&[\"first\",\"in first\",\"in first second\"],&[\"second\",\"second first\",\"second second\"],&[\"third\"],]),};pub const bar:UserPanic = UserPanic {error_msg:\"This is un fixable error\",fix_instructions: None,};", s);
292 }
293
294 #[test]
295 fn output_string_fixable() {
296 const ERR: UserPanic = UserPanic {
297 error_msg: "Error msg",
298 fix_instructions: Some(&[&["One"], &["two", "two-one", "two-two"], &["Three"]]),
299 };
300 let s = format!("{}", ERR);
301 let manual = "The Program Crashed\n\nError: Error msg\nIt seems like an error that can be fixed by you!\nPlease follow the following instructions to try and fix the Error\n\n\t1: One\n\n\t2: two\n\t\t1. two-one\n\t\t2. two-two\n\n\t3: Three\n";
302 assert_eq!(s, manual);
303 }
304
305 #[test]
306 fn output_string_unfixable() {
307 const ERR: UserPanic = UserPanic {
308 error_msg: "Unfixable Error",
309 fix_instructions: None,
310 };
311 let s = format!("{}", ERR);
312 let manual = "The Program Crashed\n\nError: Unfixable Error\nIt seems like an error that can't be fixed by you!\nPlease submit a Bug report to Developer\n";
313 assert_eq!(s, manual);
314 }
315}