Skip to main content

micromux/scheduler/
types.rs

1//! Scheduler event and command types.
2//!
3//! This module defines the types used for communication between the scheduler, services, and UI.
4//! The main types are:
5//! - [`Event`]: a one-way notification emitted by the scheduler.
6//! - [`Command`]: a request issued to the scheduler.
7//! - [`State`]: the current lifecycle state of a service.
8
9use crate::health_check::Health;
10
11/// Unique identifier for a service.
12pub type ServiceID = String;
13
14/// The lifecycle state of a service.
15#[derive(Debug)]
16pub enum State {
17    /// Service has not yet started.
18    Pending,
19    /// Service is currently starting.
20    Starting,
21    /// Service is running.
22    Running { health: Option<Health> },
23    /// Service is disabled.
24    Disabled,
25    /// Service exited with code.
26    Exited { exit_code: i32 },
27    /// Service has been killed and is awaiting exit.
28    Killed,
29}
30
31impl std::fmt::Display for State {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Self::Pending => write!(f, "Pending"),
35            Self::Starting => write!(f, "Starting"),
36            Self::Running { health: None } => write!(f, "Running"),
37            Self::Running {
38                health: Some(health),
39            } => write!(f, "Running({health})"),
40            Self::Disabled => write!(f, "Disabled"),
41            Self::Exited { exit_code } => write!(f, "Exited({exit_code})"),
42            Self::Killed => write!(f, "Killed"),
43        }
44    }
45}
46
47/// A scheduler event.
48///
49/// Events are emitted as the scheduler observes state changes or receives output from managed
50/// services.
51#[derive(Debug)]
52pub enum Event {
53    /// A service has started.
54    Started {
55        /// Service that started.
56        service_id: ServiceID,
57    },
58    /// A new log line was produced.
59    LogLine {
60        /// Service that produced this output.
61        service_id: ServiceID,
62        /// Which stream produced the output.
63        stream: OutputStream,
64        /// How this line should update the UI buffer.
65        update: LogUpdateKind,
66        /// The raw (possibly ANSI-colored) line.
67        line: String,
68    },
69    /// A healthcheck attempt is about to start.
70    HealthCheckStarted {
71        /// Service this healthcheck belongs to.
72        service_id: ServiceID,
73        /// Monotonic attempt number.
74        attempt: u64,
75        /// The command that is executed for this healthcheck.
76        command: String,
77    },
78    /// A healthcheck produced a log line.
79    HealthCheckLogLine {
80        /// Service this healthcheck belongs to.
81        service_id: ServiceID,
82        /// Monotonic attempt number.
83        attempt: u64,
84        /// Which stream produced the output.
85        stream: OutputStream,
86        /// The output line.
87        line: String,
88    },
89    /// A healthcheck finished.
90    HealthCheckFinished {
91        /// Service this healthcheck belongs to.
92        service_id: ServiceID,
93        /// Monotonic attempt number.
94        attempt: u64,
95        /// Whether the healthcheck exited successfully.
96        success: bool,
97        /// Exit code of the healthcheck process.
98        exit_code: i32,
99    },
100    /// A service was killed.
101    Killed(ServiceID),
102    /// A service exited.
103    Exited(ServiceID, i32),
104    /// A service became healthy.
105    Healthy(ServiceID),
106    /// A service became unhealthy.
107    Unhealthy(ServiceID),
108    /// A service was disabled.
109    Disabled(ServiceID),
110    /// Clear the log buffer for a service (e.g. on restart).
111    ClearLogs(ServiceID),
112}
113
114/// The kind of log update.
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum LogUpdateKind {
117    /// Append a new line to the log buffer.
118    Append,
119    /// Replace the most recent line in the log buffer.
120    ReplaceLast,
121}
122
123/// Origin stream of output.
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum OutputStream {
126    /// Standard output.
127    Stdout,
128    /// Standard error.
129    Stderr,
130}
131
132impl Event {
133    /// Returns the [`ServiceID`] associated with this event.
134    #[must_use]
135    pub fn service_id(&self) -> &ServiceID {
136        match self {
137            Self::Started { service_id }
138            | Self::LogLine { service_id, .. }
139            | Self::HealthCheckStarted { service_id, .. }
140            | Self::HealthCheckLogLine { service_id, .. }
141            | Self::HealthCheckFinished { service_id, .. }
142            | Self::Killed(service_id)
143            | Self::Exited(service_id, _)
144            | Self::Healthy(service_id)
145            | Self::Unhealthy(service_id)
146            | Self::Disabled(service_id)
147            | Self::ClearLogs(service_id) => service_id,
148        }
149    }
150}
151
152impl std::fmt::Display for Event {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        match self {
155            Self::Started { service_id, .. } => write!(f, "Started({service_id})"),
156            Self::LogLine {
157                service_id, stream, ..
158            } => write!(f, "LogLine({service_id}, {stream:?})"),
159            Self::HealthCheckStarted {
160                service_id,
161                attempt,
162                ..
163            } => write!(f, "HealthCheckStarted({service_id}, attempt={attempt})"),
164            Self::HealthCheckLogLine {
165                service_id,
166                stream,
167                attempt,
168                ..
169            } => write!(
170                f,
171                "HealthCheckLogLine({service_id}, {stream:?}, attempt={attempt})"
172            ),
173            Self::HealthCheckFinished {
174                service_id,
175                attempt,
176                success,
177                exit_code,
178            } => write!(
179                f,
180                "HealthCheckFinished({service_id}, attempt={attempt}, success={success}, exit_code={exit_code})"
181            ),
182            Self::Killed(service_id) => write!(f, "Killed({service_id})"),
183            Self::Exited(service_id, _) => write!(f, "Exited({service_id})"),
184            Self::Healthy(service_id) => write!(f, "Healthy({service_id})"),
185            Self::Unhealthy(service_id) => write!(f, "Unhealthy({service_id})"),
186            Self::Disabled(service_id) => write!(f, "Disabled({service_id})"),
187            Self::ClearLogs(service_id) => write!(f, "ClearLogs({service_id})"),
188        }
189    }
190}
191
192/// A command sent to the scheduler.
193#[derive(Debug, Clone)]
194pub enum Command {
195    /// Restart a single service.
196    Restart(ServiceID),
197    /// Restart all services.
198    RestartAll,
199    /// Disable a single service.
200    Disable(ServiceID),
201    /// Enable a single service.
202    Enable(ServiceID),
203    /// Send a raw input payload to a service.
204    SendInput(ServiceID, Vec<u8>),
205    /// Resize all PTYs.
206    ResizeAll {
207        /// Terminal width in columns.
208        cols: u16,
209        /// Terminal height in rows.
210        rows: u16,
211    },
212}