1use std::{
9 any::{type_name, TypeId},
10 collections::HashMap,
11 error::Error,
12};
13
14use downcast_rs::{impl_downcast, Downcast};
15use exams::*;
16
17pub trait Exam: Downcast {
18 fn name(&self) -> &str {
19 type_name::<Self>()
20 }
21
22 fn apply(&mut self) -> Result<(), ExamFailure>;
23}
24
25impl_downcast!(Exam);
26
27pub struct ExamFailure {
28 pub error: Box<dyn Error + 'static>,
29 pub report: Option<String>,
30}
31
32pub struct Examiner {
33 map: HashMap<TypeId, Box<dyn Exam + 'static>>,
34}
35
36impl std::fmt::Debug for Examiner {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 f.debug_struct("Examiner")
39 .field(
40 "exams",
41 &self
42 .map
43 .iter()
44 .map(|(k, v)| (k, v.name()))
45 .collect::<Vec<_>>(),
46 )
47 .finish()
48 }
49}
50
51impl Examiner {
52 pub fn new() -> Self {
53 let mut this = Self::empty();
54
55 this.add(RustfmtExam).add(TestsExam).add(ClippyExam);
56
57 this
58 }
59
60 pub fn empty() -> Self {
61 Self {
62 map: HashMap::new(),
63 }
64 }
65
66 pub fn add<T: Exam + 'static>(&mut self, exam: T) -> &mut Self {
67 let exam: Box<dyn Exam> = Box::new(exam);
68 self.map.insert(TypeId::of::<T>(), exam);
69 self
70 }
71
72 pub fn delete<T: Exam + 'static>(&mut self) -> &mut Self {
73 self.remove::<T>();
74 self
75 }
76
77 pub fn remove<T: Exam + 'static>(&mut self) -> Option<T> {
78 self.map.remove(&TypeId::of::<T>()).map(|exam| {
79 *exam
80 .downcast::<T>()
81 .map_err(|_| panic!("This downcast shouldn't fail"))
82 .unwrap()
83 })
84 }
85
86 pub fn apply(&mut self) -> Result<(), Vec<ExamFailure>> {
87 let mut failed_exams = vec![];
88
89 let mut run = |exam| {
90 apply_collecting_failures(&mut failed_exams, exam);
91 };
92
93 for exam in self.map.values_mut() {
94 println!("Applying {}...", exam.name());
95 run(exam);
96 }
97
98 if failed_exams.is_empty() {
99 Ok(())
100 } else {
101 Err(failed_exams)
102 }
103 }
104}
105
106impl Default for Examiner {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112fn apply_collecting_failures(failed_exams: &mut Vec<ExamFailure>, exam: &mut Box<dyn Exam>) {
113 if let Err(e) = exam.apply() {
114 failed_exams.push(e);
115 }
116}
117
118#[derive(Debug, thiserror::Error)]
119pub enum ExamError {
120 #[error(transparent)]
121 RustFmt(#[from] exams::RustFmtError),
122}
123
124pub mod exams;