watchexec_supervisor/job/
messages.rs

1use std::{
2	future::Future,
3	pin::Pin,
4	task::{Context, Poll},
5	time::Duration,
6};
7
8use futures::{future::select, FutureExt};
9use watchexec_signals::Signal;
10
11use crate::flag::Flag;
12
13use super::task::{
14	AsyncErrorHandler, AsyncFunc, AsyncSpawnHook, SyncErrorHandler, SyncFunc, SyncSpawnHook,
15};
16
17/// The underlying control message types for [`Job`](super::Job).
18///
19/// You may use [`Job::control()`](super::Job::control()) to send these messages directly, but in
20/// general should prefer the higher-level methods on [`Job`](super::Job) itself.
21pub enum Control {
22	/// For [`Job::start()`](super::Job::start()).
23	Start,
24	/// For [`Job::stop()`](super::Job::stop()).
25	Stop,
26	/// For [`Job::stop_with_signal()`](super::Job::stop_with_signal()).
27	GracefulStop {
28		/// Signal to send immediately
29		signal: Signal,
30		/// Time to wait before forceful termination
31		grace: Duration,
32	},
33	/// For [`Job::try_restart()`](super::Job::try_restart()).
34	TryRestart,
35	/// For [`Job::try_restart_with_signal()`](super::Job::try_restart_with_signal()).
36	TryGracefulRestart {
37		/// Signal to send immediately
38		signal: Signal,
39		/// Time to wait before forceful termination and restart
40		grace: Duration,
41	},
42	/// Internal implementation detail of [`Control::TryGracefulRestart`].
43	ContinueTryGracefulRestart,
44	/// For [`Job::signal()`](super::Job::signal()).
45	Signal(Signal),
46	/// For [`Job::delete()`](super::Job::delete()) and [`Job::delete_now()`](super::Job::delete_now()).
47	Delete,
48
49	/// For [`Job::to_wait()`](super::Job::to_wait()).
50	NextEnding,
51
52	/// For [`Job::run()`](super::Job::run()).
53	SyncFunc(SyncFunc),
54	/// For [`Job::run_async()`](super::Job::run_async()).
55	AsyncFunc(AsyncFunc),
56
57	/// For [`Job::set_spawn_hook()`](super::Job::set_spawn_hook()).
58	SetSyncSpawnHook(SyncSpawnHook),
59	/// For [`Job::set_spawn_async_hook()`](super::Job::set_spawn_async_hook()).
60	SetAsyncSpawnHook(AsyncSpawnHook),
61	/// For [`Job::unset_spawn_hook()`](super::Job::unset_spawn_hook()).
62	UnsetSpawnHook,
63	/// For [`Job::set_error_handler()`](super::Job::set_error_handler()).
64	SetSyncErrorHandler(SyncErrorHandler),
65	/// For [`Job::set_async_error_handler()`](super::Job::set_async_error_handler()).
66	SetAsyncErrorHandler(AsyncErrorHandler),
67	/// For [`Job::unset_error_handler()`](super::Job::unset_error_handler()).
68	UnsetErrorHandler,
69}
70
71impl std::fmt::Debug for Control {
72	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73		match self {
74			Self::Start => f.debug_struct("Start").finish(),
75			Self::Stop => f.debug_struct("Stop").finish(),
76			Self::GracefulStop { signal, grace } => f
77				.debug_struct("GracefulStop")
78				.field("signal", signal)
79				.field("grace", grace)
80				.finish(),
81			Self::TryRestart => f.debug_struct("TryRestart").finish(),
82			Self::TryGracefulRestart { signal, grace } => f
83				.debug_struct("TryGracefulRestart")
84				.field("signal", signal)
85				.field("grace", grace)
86				.finish(),
87			Self::ContinueTryGracefulRestart => {
88				f.debug_struct("ContinueTryGracefulRestart").finish()
89			}
90			Self::Signal(signal) => f.debug_struct("Signal").field("signal", signal).finish(),
91			Self::Delete => f.debug_struct("Delete").finish(),
92
93			Self::NextEnding => f.debug_struct("NextEnding").finish(),
94
95			Self::SyncFunc(_) => f.debug_struct("SyncFunc").finish_non_exhaustive(),
96			Self::AsyncFunc(_) => f.debug_struct("AsyncFunc").finish_non_exhaustive(),
97
98			Self::SetSyncSpawnHook(_) => f.debug_struct("SetSyncSpawnHook").finish_non_exhaustive(),
99			Self::SetAsyncSpawnHook(_) => {
100				f.debug_struct("SetSpawnAsyncHook").finish_non_exhaustive()
101			}
102			Self::UnsetSpawnHook => f.debug_struct("UnsetSpawnHook").finish(),
103			Self::SetSyncErrorHandler(_) => f
104				.debug_struct("SetSyncErrorHandler")
105				.finish_non_exhaustive(),
106			Self::SetAsyncErrorHandler(_) => f
107				.debug_struct("SetAsyncErrorHandler")
108				.finish_non_exhaustive(),
109			Self::UnsetErrorHandler => f.debug_struct("UnsetErrorHandler").finish(),
110		}
111	}
112}
113
114#[derive(Debug)]
115pub struct ControlMessage {
116	pub control: Control,
117	pub done: Flag,
118}
119
120/// Lightweight future which resolves when the corresponding control has been run.
121///
122/// Unlike most futures, tickets don't need to be polled for controls to make progress; the future
123/// is only used to signal completion. Dropping a ticket will not drop the control, so it's safe to
124/// do so if you don't care about when the control completes.
125///
126/// Tickets can be cloned, and all clones will resolve at the same time.
127#[derive(Debug, Clone)]
128pub struct Ticket {
129	pub(crate) job_gone: Flag,
130	pub(crate) control_done: Flag,
131}
132
133impl Ticket {
134	pub(crate) fn cancelled() -> Self {
135		Self {
136			job_gone: Flag::new(true),
137			control_done: Flag::new(true),
138		}
139	}
140}
141
142impl Future for Ticket {
143	type Output = ();
144
145	fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
146		Pin::new(&mut select(self.job_gone.clone(), self.control_done.clone()).map(|_| ())).poll(cx)
147	}
148}