chj_unix_util/daemon/
warrants_restart.rs1use std::{
5 io::{stderr, ErrorKind},
6 ops::Deref,
7 time::SystemTime,
8};
9
10use anyhow::Result;
11
12use crate::{
13 eval_with_default::EvalWithDefault, re_exec::current_exe,
14 timestamp_formatter::TimestampFormatter,
15};
16
17pub trait WarrantsRestart {
19 fn warrants_restart(&self) -> bool;
21}
22
23#[derive(Debug, Clone, Copy, Default, clap::Args)]
24pub struct RestartForExecutableChangeOpts {
25 #[clap(long)]
26 pub restart_on_upgrades: bool,
27
28 #[clap(long)]
33 pub no_restart_on_upgrades: bool,
34}
35
36impl EvalWithDefault for RestartForExecutableChangeOpts {
37 fn explicit_yes_and_no(&self) -> (bool, bool) {
38 let RestartForExecutableChangeOpts {
39 restart_on_upgrades,
40 no_restart_on_upgrades,
41 } = self;
42 (*restart_on_upgrades, *no_restart_on_upgrades)
43 }
44}
45
46impl RestartForExecutableChangeOpts {
47 pub fn to_restarter(
48 &self,
49 default_want_restarting: bool,
50 timestamp_formatter: TimestampFormatter,
51 ) -> Result<RestartForExecutableChange> {
52 let restart_on_upgrades = self.eval_with_default(default_want_restarting);
53 let (do_check, opt_binary_mtime) = if restart_on_upgrades {
54 let path = std::env::current_exe()?;
58 match path.metadata() {
59 Ok(m) => {
60 if let Ok(mtime) = m.modified() {
61 (true, Some(mtime))
62 } else {
63 (false, None)
64 }
65 }
66 Err(e) => {
67 let restart_if_path_shows_up_later = e.kind() == ErrorKind::NotFound;
68 (restart_if_path_shows_up_later, None)
69 }
70 }
71 } else {
72 (false, None)
73 };
74 Ok(RestartForExecutableChange {
75 timestamp_formatter,
76 do_check,
77 opt_binary_mtime,
78 })
79 }
80}
81
82#[derive(Debug, Clone)]
83pub struct RestartForExecutableChange {
84 timestamp_formatter: TimestampFormatter,
85 do_check: bool,
86 opt_binary_mtime: Option<SystemTime>,
87}
88
89impl WarrantsRestart for &RestartForExecutableChange {
90 fn warrants_restart(&self) -> bool {
91 self.do_check && {
92 match current_exe() {
93 Ok(binary) => {
94 if let Ok(metadata) = binary.metadata() {
95 if let Ok(new_mtime) = metadata.modified() {
96 let old_mtime = self.opt_binary_mtime;
97 if Some(new_mtime) != old_mtime {
99 use std::io::Write;
100 let tmp;
101 _ = writeln!(
102 &mut stderr(),
103 "the binary at {binary:?} has updated, from \
104 {} to {}, going to restart",
105 if let Some(old_mtime) = old_mtime {
106 tmp = self.timestamp_formatter.format_systemtime(old_mtime);
107 &tmp
108 } else {
109 "(none)"
110 },
111 self.timestamp_formatter.format_systemtime(new_mtime)
112 );
113 true
114 } else {
115 false
116 }
117 } else {
118 false
119 }
120 } else {
121 false
122 }
123 }
124 Err(_) => false,
125 }
126 }
127 }
128}
129
130impl RestartForExecutableChange {
131 pub fn and_config_change_opts<RestartForConfigChange: Deref<Target: WarrantsRestart>>(
132 self,
133 restart_for_config_change_opts: RestartForConfigChangeOpts,
134 default_restart_for_config_change: bool,
135 restart_for_config_change: RestartForConfigChange,
136 ) -> RestartForExecutableOrConfigChange<RestartForConfigChange> {
137 let restart_for_executable_change = self;
138 let restart_for_config_change = if restart_for_config_change_opts
139 .eval_with_default(default_restart_for_config_change)
140 {
141 Some(restart_for_config_change)
142 } else {
143 None
144 };
145
146 RestartForExecutableOrConfigChange {
147 restart_for_executable_change,
148 restart_for_config_change,
149 }
150 }
151}
152
153#[derive(Debug, Clone, Copy, Default, clap::Args)]
163pub struct RestartForConfigChangeOpts {
164 #[clap(long)]
165 pub restart_on_config_change: bool,
166
167 #[clap(long)]
172 pub no_restart_on_config_change: bool,
173}
174
175impl EvalWithDefault for RestartForConfigChangeOpts {
176 fn explicit_yes_and_no(&self) -> (bool, bool) {
177 let RestartForConfigChangeOpts {
178 restart_on_config_change,
179 no_restart_on_config_change,
180 } = self;
181 (*restart_on_config_change, *no_restart_on_config_change)
182 }
183}
184
185#[derive(Debug, Clone)]
186pub struct RestartForExecutableOrConfigChange<
187 RestartForConfigChange: Deref<Target: WarrantsRestart>,
188> {
189 restart_for_executable_change: RestartForExecutableChange,
190 restart_for_config_change: Option<RestartForConfigChange>,
191}
192
193impl<RestartForConfigChange: Deref<Target: WarrantsRestart>> WarrantsRestart
194 for RestartForExecutableOrConfigChange<RestartForConfigChange>
195{
196 fn warrants_restart(&self) -> bool {
197 (&self.restart_for_executable_change).warrants_restart()
198 || if let Some(restart_for_config_change) = &self.restart_for_config_change {
199 if restart_for_config_change.warrants_restart() {
200 use std::io::Write;
201 _ = writeln!(
202 &mut stderr(),
203 "the configuration has updated, going to restart",
204 );
205 true
206 } else {
207 false
208 }
209 } else {
210 false
211 }
212 }
213}
214
215impl<RestartForConfigChange: Deref<Target: WarrantsRestart>> Deref
217 for RestartForExecutableOrConfigChange<RestartForConfigChange>
218{
219 type Target = RestartForExecutableOrConfigChange<RestartForConfigChange>;
220
221 fn deref(&self) -> &Self::Target {
222 self
223 }
224}
225
226#[derive(Debug, Clone)]
229pub struct NoOtherRestarts;
230
231impl WarrantsRestart for NoOtherRestarts {
232 fn warrants_restart(&self) -> bool {
233 false
234 }
235}
236
237impl Deref for NoOtherRestarts {
239 type Target = NoOtherRestarts;
240
241 fn deref(&self) -> &Self::Target {
242 self
243 }
244}
245
246