pub_just/
interrupt_handler.rs

1use super::*;
2
3pub struct InterruptHandler {
4  blocks: u32,
5  interrupted: bool,
6  verbosity: Verbosity,
7}
8
9impl InterruptHandler {
10  pub fn install(verbosity: Verbosity) -> Result<(), ctrlc::Error> {
11    let mut instance = Self::instance();
12    instance.verbosity = verbosity;
13    ctrlc::set_handler(|| Self::instance().interrupt())
14  }
15
16  pub fn instance() -> MutexGuard<'static, Self> {
17    static INSTANCE: Mutex<InterruptHandler> = Mutex::new(InterruptHandler::new());
18
19    match INSTANCE.lock() {
20      Ok(guard) => guard,
21      Err(poison_error) => {
22        eprintln!(
23          "{}",
24          Error::Internal {
25            message: format!("interrupt handler mutex poisoned: {poison_error}"),
26          }
27          .color_display(Color::auto().stderr())
28        );
29        process::exit(EXIT_FAILURE);
30      }
31    }
32  }
33
34  const fn new() -> Self {
35    Self {
36      blocks: 0,
37      interrupted: false,
38      verbosity: Verbosity::default(),
39    }
40  }
41
42  fn interrupt(&mut self) {
43    self.interrupted = true;
44
45    if self.blocks > 0 {
46      return;
47    }
48
49    Self::exit();
50  }
51
52  fn exit() {
53    process::exit(130);
54  }
55
56  pub fn block(&mut self) {
57    self.blocks += 1;
58  }
59
60  pub fn unblock(&mut self) {
61    if self.blocks == 0 {
62      if self.verbosity.loud() {
63        eprintln!(
64          "{}",
65          Error::Internal {
66            message: "attempted to unblock interrupt handler, but handler was not blocked"
67              .to_owned(),
68          }
69          .color_display(Color::auto().stderr())
70        );
71      }
72      process::exit(EXIT_FAILURE);
73    }
74
75    self.blocks -= 1;
76
77    if self.interrupted {
78      Self::exit();
79    }
80  }
81
82  pub fn guard<T, F: FnOnce() -> T>(function: F) -> T {
83    let _guard = InterruptGuard::new();
84    function()
85  }
86}