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, since nix's Signal doesn't implement [Display] and
189/// [std::str::FromStr].
190#[derive(
191    PartialEq, Eq, Clone, Debug, Deserialize, Serialize, Display, EnumString, VariantNames,
192)]
193#[strum(ascii_case_insensitive)]
194pub enum Signal {
195    #[strum(serialize = "sigint", serialize = "int", serialize = "2")]
196    SigInt,
197    #[strum(serialize = "sigkill", serialize = "kill", serialize = "9")]
198    SigKill,
199    #[strum(serialize = "sigterm", serialize = "term", serialize = "15")]
200    SigTerm,
201    #[strum(serialize = "sigcont", serialize = "cont", serialize = "18")]
202    SigCont,
203    #[strum(serialize = "sigstop", serialize = "stop", serialize = "19")]
204    SigStop,
205}
206
207#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
208pub struct KillRequest {
209    pub tasks: TaskSelection,
210    pub signal: Option<Signal>,
211}
212impl_into_request!(KillRequest, Request::Kill);
213
214#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
215pub struct SendRequest {
216    pub task_id: usize,
217    pub input: String,
218}
219impl_into_request!(SendRequest, Request::Send);
220
221#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
222pub enum EnvRequest {
223    Set {
224        task_id: usize,
225        key: String,
226        value: String,
227    },
228    Unset {
229        task_id: usize,
230        key: String,
231    },
232}
233impl_into_request!(EnvRequest, Request::Env);
234
235#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
236pub enum GroupRequest {
237    Add {
238        name: String,
239        parallel_tasks: Option<usize>,
240    },
241    Remove(String),
242    List,
243}
244impl_into_request!(GroupRequest, Request::Group);
245
246#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
247pub enum ResetTarget {
248    // Reset all groups
249    All,
250    // Reset a list of specific groups
251    Groups(Vec<String>),
252}
253
254#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
255pub struct ResetRequest {
256    pub target: ResetTarget,
257}
258impl_into_request!(ResetRequest, Request::Reset);
259
260#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
261pub struct CleanRequest {
262    pub successful_only: bool,
263
264    pub group: Option<String>,
265}
266impl_into_request!(CleanRequest, Request::Clean);
267
268/// Determines which type of shutdown we're dealing with.
269#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
270pub enum ShutdownRequest {
271    /// Emergency is most likely a system unix signal or a CTRL+C in a terminal.
272    Emergency,
273    /// Graceful is user initiated and expected.
274    Graceful,
275}
276impl_into_request!(ShutdownRequest, Request::DaemonShutdown);
277
278/// Request the live streaming of a set of running tasks.
279///
280/// **WARNING**:
281/// Even though this type currently accepts a TaskSelection, only
282/// `TaskSelection::TaskIds(vec![])` and `TaskSelection::TaskIds(vec![id])` are accepted.
283/// We already use this format in preparation for <https://github.com/Nukesor/pueue/issues/614>
284/// That way we can stay forwards compatible without having to break the API.
285#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
286pub struct StreamRequest {
287    pub tasks: TaskSelection,
288    pub lines: Option<usize>,
289}
290impl_into_request!(StreamRequest, Request::Stream);
291
292/// Request logs for specific tasks.
293///
294/// `tasks` specifies the requested tasks.
295/// `send_logs` Determines whether logs should be sent at all.
296/// `lines` Determines whether only a few lines of log should be returned.
297#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
298pub struct LogRequest {
299    pub tasks: TaskSelection,
300    pub send_logs: bool,
301    pub lines: Option<usize>,
302}
303impl_into_request!(LogRequest, Request::Log);
304
305#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
306pub struct ParallelRequest {
307    pub parallel_tasks: usize,
308    pub group: String,
309}
310impl_into_request!(ParallelRequest, Request::Parallel);