watchexec_supervisor/job/
state.rs1use std::{sync::Arc, time::Instant};
2
3#[cfg(not(test))]
4use process_wrap::tokio::TokioChildWrapper;
5use process_wrap::tokio::TokioCommandWrap;
6use tracing::trace;
7use watchexec_events::ProcessEnd;
8
9use crate::command::Command;
10
11#[derive(Debug)]
21#[cfg_attr(test, derive(Clone))]
22pub enum CommandState {
23 Pending,
25
26 Running {
30 #[cfg(test)]
32 child: super::TestChild,
33
34 #[cfg(not(test))]
36 child: Box<dyn TokioChildWrapper>,
37
38 started: Instant,
40 },
41
42 Finished {
44 status: ProcessEnd,
46
47 started: Instant,
49
50 finished: Instant,
52 },
53}
54
55impl CommandState {
56 #[must_use]
58 pub const fn is_pending(&self) -> bool {
59 matches!(self, Self::Pending)
60 }
61
62 #[must_use]
64 pub const fn is_running(&self) -> bool {
65 matches!(self, Self::Running { .. })
66 }
67
68 #[must_use]
70 pub const fn is_finished(&self) -> bool {
71 matches!(self, Self::Finished { .. })
72 }
73
74 #[cfg_attr(test, allow(unused_mut, unused_variables))]
75 pub(crate) fn spawn(
76 &mut self,
77 command: Arc<Command>,
78 mut spawnable: TokioCommandWrap,
79 ) -> std::io::Result<bool> {
80 if let Self::Running { .. } = self {
81 trace!("command running, not spawning again");
82 return Ok(false);
83 }
84
85 trace!(?command, "spawning command");
86
87 #[cfg(test)]
88 let child = super::TestChild::new(command)?;
89
90 #[cfg(not(test))]
91 let child = spawnable.spawn()?;
92
93 *self = Self::Running {
94 child,
95 started: Instant::now(),
96 };
97 Ok(true)
98 }
99
100 #[must_use]
101 pub(crate) fn reset(&mut self) -> Self {
102 trace!(?self, "resetting command state");
103 match self {
104 Self::Pending => Self::Pending,
105 Self::Finished {
106 status,
107 started,
108 finished,
109 ..
110 } => {
111 let copy = Self::Finished {
112 status: *status,
113 started: *started,
114 finished: *finished,
115 };
116
117 *self = Self::Pending;
118 copy
119 }
120 Self::Running { started, .. } => {
121 let copy = Self::Finished {
122 status: ProcessEnd::Continued,
123 started: *started,
124 finished: Instant::now(),
125 };
126
127 *self = Self::Pending;
128 copy
129 }
130 }
131 }
132
133 pub(crate) async fn wait(&mut self) -> std::io::Result<bool> {
134 if let Self::Running { child, started } = self {
135 let end = Box::into_pin(child.wait()).await?;
136 *self = Self::Finished {
137 status: end.into(),
138 started: *started,
139 finished: Instant::now(),
140 };
141 Ok(true)
142 } else {
143 Ok(false)
144 }
145 }
146}