collective_score_client/
check.rs1use std::{
2 borrow::Cow,
3 fmt,
4 io::{stdout, Write},
5};
6use typed_builder::TypedBuilder;
7use yansi::Paint;
8
9use crate::validator::Validator;
10
11pub type Message = Cow<'static, str>;
12
13#[derive(TypedBuilder)]
14pub struct Check<T>
15where
16 T: Validator,
17{
18 #[builder(setter(into))]
19 description: Message,
20 validator: T,
21 #[builder(default, setter(strip_option, into))]
22 hint: Option<Message>,
23}
24
25impl<T> Check<T>
26where
27 T: Validator + 'static,
28{
29 pub fn into_box(self) -> Box<dyn RunnableCheck> {
30 Box::new(self)
31 }
32}
33
34pub struct CheckErr;
35
36impl fmt::Debug for CheckErr {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 writeln!(f, "Check failed!")
39 }
40}
41
42pub trait RunnableCheck {
43 fn run(&self) -> Result<(), CheckErr>;
44}
45
46impl<T> RunnableCheck for Check<T>
47where
48 T: Validator,
49{
50 fn run(&self) -> Result<(), CheckErr> {
51 let mut stdout = stdout().lock();
52
53 write!(stdout, "{}… ", self.description.bold().blue().underline()).unwrap();
54 stdout.flush().unwrap();
55
56 if let Err(e) = self.validator.validate() {
57 writeln!(stdout, "{}", "Failed".bold().red()).unwrap();
58
59 writeln!(stdout, "\n{e:?}").unwrap();
60
61 if let Some(hint) = &self.hint {
62 let separator = "–––––––".bold().yellow();
63
64 writeln!(
65 stdout,
66 "\n{} 💡\n{separator}\n{hint}\n{separator}",
67 "Hint".bold().yellow()
68 )
69 .unwrap();
70 }
71
72 return Err(CheckErr);
73 }
74
75 writeln!(stdout, "{}\n", "OK".bold().green()).unwrap();
76
77 stdout.flush().unwrap();
78
79 Ok(())
80 }
81}
82
83impl<const N: usize> RunnableCheck for [Box<dyn RunnableCheck>; N] {
84 fn run(&self) -> Result<(), CheckErr> {
85 for check in self {
86 if check.run().is_err() {
87 return Err(CheckErr);
88 }
89 }
90
91 Ok(())
92 }
93}
94
95pub type Task = (&'static str, Box<dyn RunnableCheck>);
96
97pub trait IntoTask: RunnableCheck + Sized + 'static {
98 fn into_task(self, task_name: &'static str) -> Task {
99 (task_name, Box::new(self))
100 }
101}
102
103impl<T> IntoTask for T where T: RunnableCheck + 'static {}