Skip to main content

pueue_lib/message/
request.rs

1use std::{collections::HashMap, path::PathBuf};
2
3use chrono::prelude::*;
4use serde::{Deserialize, Serialize};
5use strum::{Display, EnumString, VariantNames};
6
7use crate::message::EditableTask;
8
9/// Macro to simplify creating [From] implementations for each variant-contained
10/// Request; e.g. `impl_into_request!(AddRequest, Request::Add)` to make it possible
11/// to use `AddRequest::into()` and get a [Request::Add] value.
12macro_rules! impl_into_request {
13    ($inner:ident, $variant:expr) => {
14        impl From<$inner> for Request {
15            fn from(message: $inner) -> Self {
16                $variant(message)
17            }
18        }
19    };
20}
21
22/// This is the message for messages sent **to** the daemon. \
23/// Everything that's send by the client is represented using by this enum.
24#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
25pub enum Request {
26    /// Add a new task to the daemon.
27    Add(AddRequest),
28    /// Remove non-running/paused tasks.
29    Remove(Vec<usize>),
30    /// Switch two enqueued/stashed tasks.
31    Switch(SwitchRequest),
32    /// Stash a task or schedule it for enqueue.
33    Stash(StashRequest),
34    /// Take a stashed task and enqueue it.
35    Enqueue(EnqueueRequest),
36
37    /// Start/unpause a [`TaskSelection`].
38    Start(StartRequest),
39    /// Restart a set of finished or failed task.
40    Restart(RestartRequest),
41    /// Pause a [`TaskSelection`].
42    Pause(PauseRequest),
43    /// Kill a [`TaskSelection`].
44    Kill(KillRequest),
45
46    /// Used to send some input to a process's stdin
47    Send(SendRequest),
48
49    /// The first part of the three-step protocol to edit a task.
50    /// This one requests an edit from the daemon.
51    EditRequest(Vec<usize>),
52    /// This is send by the client if something went wrong during the editing process.
53    /// The daemon will go ahead and restore the task's old state.
54    EditRestore(Vec<usize>),
55    /// The client sends the edited details to the daemon.
56    EditedTasks(Vec<EditableTask>),
57
58    /// Un/-set environment variables for specific tasks.
59    Env(EnvRequest),
60
61    Group(GroupRequest),
62
63    /// Used to set parallel tasks for a specific group
64    Parallel(ParallelRequest),
65
66    /// Request the daemon's state
67    Status,
68    /// Request logs of a set of tasks.
69    Log(LogRequest),
70
71    /// The client requests a continuous stream of a task's log.
72    Stream(StreamRequest),
73
74    /// Reset the daemon
75    Reset(ResetRequest),
76    /// Tell the daemon to clean finished tasks
77    Clean(CleanRequest),
78    /// Initiate shutdown on the daemon.
79    DaemonShutdown(ShutdownRequest),
80}
81
82/// This enum is used to express a selection of tasks.
83/// As commands can be executed on various sets of tasks, we need some kind of datastructure to
84/// explicitly and unambiguously specify the selection.
85#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
86pub enum TaskSelection {
87    TaskIds(Vec<usize>),
88    Group(String),
89    All,
90}
91
92#[derive(PartialEq, Eq, Clone, Default, Deserialize, Serialize)]
93pub struct AddRequest {
94    pub command: String,
95    pub path: PathBuf,
96    pub envs: HashMap<String, String>,
97    pub start_immediately: bool,
98    pub stashed: bool,
99    pub group: String,
100    pub enqueue_at: Option<DateTime<Local>>,
101    pub dependencies: Vec<usize>,
102    pub priority: Option<i32>,
103    pub label: Option<String>,
104}
105
106/// We use a custom `Debug` implementation for [AddRequest], as the `envs` field just has
107/// too much info in it and makes the log output much too verbose.
108///
109/// Furthermore, there might be secrets in the environment, resulting in a possible leak
110/// if users copy-paste their log output for debugging.
111impl std::fmt::Debug for AddRequest {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        f.debug_struct("Task")
114            .field("command", &self.command)
115            .field("path", &self.path)
116            .field("envs", &"hidden")
117            .field("start_immediately", &self.start_immediately)
118            .field("stashed", &self.stashed)
119            .field("group", &self.group)
120            .field("enqueue_at", &self.enqueue_at)
121            .field("dependencies", &self.dependencies)
122            .field("label", &self.label)
123            .finish()
124    }
125}
126impl_into_request!(AddRequest, Request::Add);
127
128#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
129pub struct SwitchRequest {
130    pub task_id_1: usize,
131    pub task_id_2: usize,
132}
133impl_into_request!(SwitchRequest, Request::Switch);
134
135#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
136pub struct StashRequest {
137    pub tasks: TaskSelection,
138    pub enqueue_at: Option<DateTime<Local>>,
139}
140impl_into_request!(StashRequest, Request::Stash);
141
142#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
143pub struct EnqueueRequest {
144    pub tasks: TaskSelection,
145    pub enqueue_at: Option<DateTime<Local>>,
146}
147impl_into_request!(EnqueueRequest, Request::Enqueue);
148
149#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
150pub struct StartRequest {
151    pub tasks: TaskSelection,
152}
153impl_into_request!(StartRequest, Request::Start);
154
155/// The messages used to restart tasks.
156/// It's possible to update the command and paths when restarting tasks.
157#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
158pub struct RestartRequest {
159    pub tasks: Vec<TaskToRestart>,
160    pub start_immediately: bool,
161    pub stashed: bool,
162}
163impl_into_request!(RestartRequest, Request::Restart);
164
165#[derive(PartialEq, Eq, Clone, Debug, Default, Deserialize, Serialize)]
166pub struct TaskToRestart {
167    pub task_id: usize,
168    /// Restart the task with an updated command.
169    pub original_command: String,
170    /// Restart the task with an updated path.
171    pub path: PathBuf,
172    /// Restart the task with an updated label.
173    pub label: Option<String>,
174    /// Restart the task with an updated priority.
175    pub priority: i32,
176}
177
178#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
179pub struct PauseRequest {
180    pub tasks: TaskSelection,
181    pub wait: bool,
182}
183impl_into_request!(PauseRequest, Request::Pause);
184
185/// This is a small custom Enum for all currently supported unix signals.
186/// Supporting all unix signals would be a mess, since there is a LOT of them.
187///
188/// This is also needed for usage in clap.
189#[derive(
190    PartialEq, Eq, Clone, Debug, Deserialize, Serialize, Display, EnumString, VariantNames,
191)]
192#[strum(ascii_case_insensitive)]
193pub enum Signal {
194    #[strum(serialize = "sigint", serialize = "int", serialize = "2")]
195    SigInt,
196    #[strum(serialize = "sigkill", serialize = "kill", serialize = "9")]
197    SigKill,
198    #[strum(serialize = "sigterm", serialize = "term", serialize = "15")]
199    SigTerm,
200    #[strum(serialize = "sigcont", serialize = "cont", serialize = "18")]
201    SigCont,
202    #[strum(serialize = "sigstop", serialize = "stop", serialize = "19")]
203    SigStop,
204}
205
206#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
207pub struct KillRequest {
208    pub tasks: TaskSelection,
209    pub signal: Option<Signal>,
210}
211impl_into_request!(KillRequest, Request::Kill);
212
213#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
214pub struct SendRequest {
215    pub task_id: usize,
216    pub input: String,
217}
218impl_into_request!(SendRequest, Request::Send);
219
220#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
221pub enum EnvRequest {
222    Set {
223        task_id: usize,
224        key: String,
225        value: String,
226    },
227    Unset {
228        task_id: usize,
229        key: String,
230    },
231}
232impl_into_request!(EnvRequest, Request::Env);
233
234#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
235pub enum GroupRequest {
236    Add {
237        name: String,
238        parallel_tasks: Option<usize>,
239    },
240    Remove(String),
241    List,
242}
243impl_into_request!(GroupRequest, Request::Group);
244
245#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
246pub enum ResetTarget {
247    // Reset all groups
248    All,
249    // Reset a list of specific groups
250    Groups(Vec<String>),
251}
252
253#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
254pub struct ResetRequest {
255    pub target: ResetTarget,
256}
257impl_into_request!(ResetRequest, Request::Reset);
258
259#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
260pub struct CleanRequest {
261    pub successful_only: bool,
262
263    pub group: Option<String>,
264}
265impl_into_request!(CleanRequest, Request::Clean);
266
267/// Determines which type of shutdown we're dealing with.
268#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
269pub enum ShutdownRequest {
270    /// Emergency is most likely a system unix signal or a CTRL+C in a terminal.
271    Emergency,
272    /// Graceful is user initiated and expected.
273    Graceful,
274}
275impl_into_request!(ShutdownRequest, Request::DaemonShutdown);
276
277/// Request the live streaming of a set of running tasks.
278///
279/// **WARNING**:
280/// Even though this type currently accepts a TaskSelection, only
281/// `TaskSelection::TaskIds(vec![])` and `TaskSelection::TaskIds(vec![id])` are accepted.
282/// We already use this format in preparation for <https://github.com/Nukesor/pueue/issues/614>
283/// That way we can stay forwards compatible without having to break the API.
284#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
285pub struct StreamRequest {
286    pub tasks: TaskSelection,
287    pub lines: Option<usize>,
288}
289impl_into_request!(StreamRequest, Request::Stream);
290
291/// Request logs for specific tasks.
292///
293/// `tasks` specifies the requested tasks.
294/// `send_logs` Determines whether logs should be sent at all.
295/// `lines` Determines whether only a few lines of log should be returned.
296#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
297pub struct LogRequest {
298    pub tasks: TaskSelection,
299    pub send_logs: bool,
300    pub lines: Option<usize>,
301}
302impl_into_request!(LogRequest, Request::Log);
303
304#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
305pub struct ParallelRequest {
306    pub parallel_tasks: usize,
307    pub group: String,
308}
309impl_into_request!(ParallelRequest, Request::Parallel);