diff --git a/CHANGELOG.md b/CHANGELOG.md
index be2c3166..9536b51e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -51,6 +51,8 @@ TLDR: The new task state representation is more verbose but significantly cleane
### Add
- Add `--all` and `--group` to `pueue log`. [#509](https://github.com/Nukesor/pueue/issues/509)
+- Add `--all` and `--group` to `pueue enqueue`. [#558](https://github.com/Nukesor/pueue/issues/558)
+- Add `--all` and `--group` to `pueue stash`. [#558](https://github.com/Nukesor/pueue/issues/558)
- Add `pueue reset --groups [group_names]` to allow resetting individual groups. [#482](https://github.com/Nukesor/pueue/issues/482) \
This also refactors the way resets are done internally, resulting in a cleaner code architecture.
- Ability to set the Unix socket permissions through the new `unix_socket_permissions` configuration option. [#544](https://github.com/Nukesor/pueue/pull/544)
diff --git a/docs/Pueue State Diagram.svg b/docs/Pueue State Diagram.svg
new file mode 100644
index 00000000..cbc23436
--- /dev/null
+++ b/docs/Pueue State Diagram.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/pueue/src/client/cli.rs b/pueue/src/client/cli.rs
index 463f623b..df2d2096 100644
--- a/pueue/src/client/cli.rs
+++ b/pueue/src/client/cli.rs
@@ -96,10 +96,23 @@ pub enum SubCommand {
/// You have to enqueue them or start them by hand.
Stash {
/// Stash these specific tasks.
- #[arg(required = true)]
task_ids: Vec,
+
+ /// Stash all queued tasks in a group
+ #[arg(short, long, conflicts_with = "all")]
+ group: Option,
+
+ /// Stash all queued tasks across all groups.
+ #[arg(short, long)]
+ all: bool,
+
+ /// Delay enqueuing these tasks until 'delay' elapses. See DELAY FORMAT below.
+ #[arg(name = "delay", short, long, value_parser = parse_delay_until)]
+ delay_until: Option>,
},
/// Enqueue stashed tasks. They'll be handled normally afterwards.
+ ///
+ /// Enqueues all stashed task in the default group if no arguments are given.
#[command(after_help = "DELAY FORMAT:
The --delay argument must be either a number of seconds or a \"date expression\" similar to GNU \
@@ -126,6 +139,14 @@ pub enum SubCommand {
/// Enqueue these specific tasks.
task_ids: Vec,
+ /// Enqueue all stashed tasks in a group
+ #[arg(short, long, conflicts_with = "all")]
+ group: Option,
+
+ /// Enqueue all stashed tasks across all groups.
+ #[arg(short, long)]
+ all: bool,
+
/// Delay enqueuing these tasks until 'delay' elapses. See DELAY FORMAT below.
#[arg(name = "delay", short, long, value_parser = parse_delay_until)]
delay_until: Option>,
diff --git a/pueue/src/client/client.rs b/pueue/src/client/client.rs
index d20bce36..cbbe54da 100644
--- a/pueue/src/client/client.rs
+++ b/pueue/src/client/client.rs
@@ -456,7 +456,19 @@ impl Client {
}
Message::Remove(task_ids.clone())
}
- SubCommand::Stash { task_ids } => Message::Stash(task_ids.clone()),
+ SubCommand::Stash {
+ task_ids,
+ group,
+ all,
+ delay_until,
+ } => {
+ let selection = selection_from_params(*all, group, task_ids);
+ StashMessage {
+ tasks: selection,
+ enqueue_at: *delay_until,
+ }
+ .into()
+ }
SubCommand::Switch {
task_id_1,
task_id_2,
@@ -467,10 +479,15 @@ impl Client {
.into(),
SubCommand::Enqueue {
task_ids,
+ group,
+ all,
delay_until,
- } => EnqueueMessage {
- task_ids: task_ids.clone(),
- enqueue_at: *delay_until,
+ } => {
+ let selection = selection_from_params(*all, group, task_ids);
+ EnqueueMessage {
+ tasks: selection,
+ enqueue_at: *delay_until,
+ }
}
.into(),
SubCommand::Start {
diff --git a/pueue/src/daemon/network/message_handler/add.rs b/pueue/src/daemon/network/message_handler/add.rs
index 0846eff6..8310b3bf 100644
--- a/pueue/src/daemon/network/message_handler/add.rs
+++ b/pueue/src/daemon/network/message_handler/add.rs
@@ -83,7 +83,7 @@ pub fn add_task(settings: &Settings, state: &SharedState, message: AddMessage) -
let mut response = if message.print_task_id {
task_id.to_string()
} else if let Some(enqueue_at) = message.enqueue_at {
- let enqueue_at = enqueue_at.format("%Y-%m-%d %H:%M:%S");
+ let enqueue_at = format_datetime(settings, &enqueue_at);
format!("New task added (id {task_id}). It will be enqueued at {enqueue_at}")
} else {
format!("New task added (id {task_id}).")
diff --git a/pueue/src/daemon/network/message_handler/enqueue.rs b/pueue/src/daemon/network/message_handler/enqueue.rs
index a1dfb663..e3d8c72c 100644
--- a/pueue/src/daemon/network/message_handler/enqueue.rs
+++ b/pueue/src/daemon/network/message_handler/enqueue.rs
@@ -1,25 +1,62 @@
use chrono::Local;
-use pueue_lib::network::message::*;
-use pueue_lib::state::SharedState;
-use pueue_lib::task::TaskStatus;
+use pueue_lib::{
+ network::message::*, settings::Settings, state::SharedState, success_msg, task::TaskStatus,
+};
use crate::daemon::network::response_helper::*;
+use super::format_datetime;
+
/// Invoked when calling `pueue enqueue`.
/// Enqueue specific stashed tasks.
-pub fn enqueue(state: &SharedState, message: EnqueueMessage) -> Message {
+pub fn enqueue(settings: &Settings, state: &SharedState, message: EnqueueMessage) -> Message {
let mut state = state.lock().unwrap();
- let filtered_tasks = state.filter_tasks(
- |task| {
- matches!(
- task.status,
- TaskStatus::Stashed { .. } | TaskStatus::Locked { .. }
- )
- },
- Some(message.task_ids),
- );
-
- for task_id in &filtered_tasks.matching_ids {
+ // Get the affected task ids, based on the task selection.
+ let selected_task_ids = match message.tasks {
+ TaskSelection::TaskIds(ref task_ids) => state
+ .tasks
+ .iter()
+ .filter(|(task_id, task)| {
+ if !task_ids.contains(task_id) {
+ return false;
+ }
+
+ matches!(
+ task.status,
+ TaskStatus::Stashed { .. } | TaskStatus::Locked { .. }
+ )
+ })
+ .map(|(task_id, _)| *task_id)
+ .collect::>(),
+ TaskSelection::Group(ref group) => state
+ .tasks
+ .iter()
+ .filter(|(_, task)| {
+ if task.group != *group {
+ return false;
+ }
+
+ matches!(
+ task.status,
+ TaskStatus::Stashed { .. } | TaskStatus::Locked { .. }
+ )
+ })
+ .map(|(task_id, _)| *task_id)
+ .collect::>(),
+ TaskSelection::All => state
+ .tasks
+ .iter()
+ .filter(|(_, task)| {
+ matches!(
+ task.status,
+ TaskStatus::Stashed { .. } | TaskStatus::Locked { .. }
+ )
+ })
+ .map(|(task_id, _)| *task_id)
+ .collect::>(),
+ };
+
+ for task_id in &selected_task_ids {
// We just checked that they're there and the state is locked. It's safe to unwrap.
let task = state.tasks.get_mut(task_id).expect("Task should be there.");
@@ -36,12 +73,48 @@ pub fn enqueue(state: &SharedState, message: EnqueueMessage) -> Message {
}
}
- let text = if let Some(enqueue_at) = message.enqueue_at {
- let enqueue_at = enqueue_at.format("%Y-%m-%d %H:%M:%S");
- format!("Tasks will be enqueued at {enqueue_at}")
- } else {
- String::from("Tasks are enqueued")
- };
+ // Construct a response depending on the selected tasks.
+ if let Some(enqueue_at) = &message.enqueue_at {
+ let enqueue_at = format_datetime(settings, enqueue_at);
- compile_task_response(&text, filtered_tasks)
+ match &message.tasks {
+ TaskSelection::TaskIds(task_ids) => task_action_response_helper(
+ &format!("Stashed tasks will be enqueued at {enqueue_at}"),
+ task_ids.clone(),
+ |task| {
+ matches!(
+ task.status,
+ TaskStatus::Stashed { .. } | TaskStatus::Locked { .. }
+ )
+ },
+ &state,
+ ),
+ TaskSelection::Group(group) => {
+ success_msg!("Enqueue stashed tasks of group {group} at {enqueue_at}.",)
+ }
+ TaskSelection::All => {
+ success_msg!("Enqueue all stashed tasks at {enqueue_at}.",)
+ }
+ }
+ } else {
+ match &message.tasks {
+ TaskSelection::TaskIds(task_ids) => task_action_response_helper(
+ "Stashed tasks have been enqueued",
+ task_ids.clone(),
+ |task| {
+ matches!(
+ task.status,
+ TaskStatus::Stashed { .. } | TaskStatus::Locked { .. }
+ )
+ },
+ &state,
+ ),
+ TaskSelection::Group(group) => {
+ success_msg!("All stashed tasks of group \"{group}\" have been enqueued.")
+ }
+ TaskSelection::All => {
+ success_msg!("All stashed tasks have been enqueued.")
+ }
+ }
+ }
}
diff --git a/pueue/src/daemon/network/message_handler/mod.rs b/pueue/src/daemon/network/message_handler/mod.rs
index f7404609..2dd2d781 100644
--- a/pueue/src/daemon/network/message_handler/mod.rs
+++ b/pueue/src/daemon/network/message_handler/mod.rs
@@ -1,5 +1,6 @@
use std::fmt::Display;
+use chrono::{DateTime, Local};
use pueue_lib::failure_msg;
use pueue_lib::network::message::*;
use pueue_lib::settings::Settings;
@@ -31,7 +32,7 @@ pub fn handle_message(message: Message, state: &SharedState, settings: &Settings
Message::Edit(message) => edit::edit(settings, state, message),
Message::EditRequest(task_id) => edit::edit_request(state, task_id),
Message::EditRestore(task_id) => edit::edit_restore(state, task_id),
- Message::Enqueue(message) => enqueue::enqueue(state, message),
+ Message::Enqueue(message) => enqueue::enqueue(settings, state, message),
Message::Group(message) => group::group(settings, state, message),
Message::Kill(message) => kill::kill(settings, state, message),
Message::Log(message) => log::get_log(settings, state, message),
@@ -42,7 +43,7 @@ pub fn handle_message(message: Message, state: &SharedState, settings: &Settings
Message::Restart(message) => restart::restart_multiple(settings, state, message),
Message::Send(message) => send::send(state, message),
Message::Start(message) => start::start(settings, state, message),
- Message::Stash(task_ids) => stash::stash(state, task_ids),
+ Message::Stash(message) => stash::stash(settings, state, message),
Message::Switch(message) => switch::switch(settings, state, message),
Message::Status => get_status(state),
_ => create_failure_message("Not yet implemented"),
@@ -56,6 +57,16 @@ fn get_status(state: &SharedState) -> Message {
Message::StatusResponse(Box::new(state))
}
+// If the enqueue at time is today, only show the time. Otherwise, include the date.
+fn format_datetime(settings: &Settings, enqueue_at: &DateTime) -> String {
+ let format_string = if enqueue_at.date_naive() == Local::now().date_naive() {
+ &settings.client.status_time_format
+ } else {
+ &settings.client.status_datetime_format
+ };
+ enqueue_at.format(format_string).to_string()
+}
+
fn ok_or_failure_message(result: Result) -> Result {
match result {
Ok(inner) => Ok(inner),
diff --git a/pueue/src/daemon/network/message_handler/pause.rs b/pueue/src/daemon/network/message_handler/pause.rs
index e9f11860..428ca26d 100644
--- a/pueue/src/daemon/network/message_handler/pause.rs
+++ b/pueue/src/daemon/network/message_handler/pause.rs
@@ -20,7 +20,7 @@ pub fn pause(settings: &Settings, state: &SharedState, message: PauseMessage) ->
// Construct a response depending on the selected tasks.
let response = match &message.tasks {
TaskSelection::TaskIds(task_ids) => task_action_response_helper(
- "Tasks are being paused",
+ "Tasks have been paused",
task_ids.clone(),
|task| matches!(task.status, TaskStatus::Running { .. }),
&state,
@@ -28,7 +28,7 @@ pub fn pause(settings: &Settings, state: &SharedState, message: PauseMessage) ->
TaskSelection::Group(group) => {
success_msg!("Group \"{group}\" is being paused.")
}
- TaskSelection::All => success_msg!("All queues are being paused."),
+ TaskSelection::All => success_msg!("All groups are being paused."),
};
// Actually execute the command
diff --git a/pueue/src/daemon/network/message_handler/restart.rs b/pueue/src/daemon/network/message_handler/restart.rs
index ba4b0f82..2d337dab 100644
--- a/pueue/src/daemon/network/message_handler/restart.rs
+++ b/pueue/src/daemon/network/message_handler/restart.rs
@@ -26,7 +26,7 @@ pub fn restart_multiple(
// We have to compile the response beforehand.
// Otherwise we no longer know which tasks, were actually capable of being being restarted.
let response = task_action_response_helper(
- "Tasks restarted",
+ "Tasks has restarted",
task_ids.clone(),
|task| task.is_done(),
&state,
diff --git a/pueue/src/daemon/network/message_handler/start.rs b/pueue/src/daemon/network/message_handler/start.rs
index 4428376e..838aadc3 100644
--- a/pueue/src/daemon/network/message_handler/start.rs
+++ b/pueue/src/daemon/network/message_handler/start.rs
@@ -20,7 +20,7 @@ pub fn start(settings: &Settings, state: &SharedState, message: StartMessage) ->
let response = match &message.tasks {
TaskSelection::TaskIds(task_ids) => task_action_response_helper(
- "Tasks are being started",
+ "Tasks have been started/resumed",
task_ids.clone(),
|task| {
matches!(
@@ -35,7 +35,7 @@ pub fn start(settings: &Settings, state: &SharedState, message: StartMessage) ->
TaskSelection::Group(group) => {
success_msg!("Group \"{group}\" is being resumed.")
}
- TaskSelection::All => success_msg!("All queues are being resumed."),
+ TaskSelection::All => success_msg!("All groups are being resumed."),
};
if let Message::Success(_) = response {
diff --git a/pueue/src/daemon/network/message_handler/stash.rs b/pueue/src/daemon/network/message_handler/stash.rs
index d01b4505..dcc3356e 100644
--- a/pueue/src/daemon/network/message_handler/stash.rs
+++ b/pueue/src/daemon/network/message_handler/stash.rs
@@ -1,29 +1,112 @@
-use pueue_lib::network::message::*;
-use pueue_lib::state::SharedState;
-use pueue_lib::task::TaskStatus;
+use pueue_lib::{
+ network::message::*, settings::Settings, state::SharedState, success_msg, task::TaskStatus,
+};
use crate::daemon::network::response_helper::*;
+use super::format_datetime;
+
/// Invoked when calling `pueue stash`.
/// Stash specific queued tasks.
/// They won't be executed until they're enqueued or explicitly started.
-pub fn stash(state: &SharedState, task_ids: Vec) -> Message {
+pub fn stash(settings: &Settings, state: &SharedState, message: StashMessage) -> Message {
let mut state = state.lock().unwrap();
- let filtered_tasks = state.filter_tasks(
- |task| {
- matches!(
- task.status,
- TaskStatus::Queued { .. } | TaskStatus::Locked { .. }
- )
- },
- Some(task_ids),
- );
-
- for task_id in &filtered_tasks.matching_ids {
- if let Some(ref mut task) = state.tasks.get_mut(task_id) {
- task.status = TaskStatus::Stashed { enqueue_at: None };
- }
+ // Get the affected task ids, based on the task selection.
+ let selected_task_ids = match message.tasks {
+ TaskSelection::TaskIds(ref task_ids) => state
+ .tasks
+ .iter()
+ .filter(|(task_id, task)| {
+ if !task_ids.contains(task_id) {
+ return false;
+ }
+
+ matches!(
+ task.status,
+ TaskStatus::Queued { .. } | TaskStatus::Locked { .. }
+ )
+ })
+ .map(|(task_id, _)| *task_id)
+ .collect::>(),
+ TaskSelection::Group(ref group) => state
+ .tasks
+ .iter()
+ .filter(|(_, task)| {
+ if task.group != *group {
+ return false;
+ }
+
+ matches!(
+ task.status,
+ TaskStatus::Queued { .. } | TaskStatus::Locked { .. }
+ )
+ })
+ .map(|(task_id, _)| *task_id)
+ .collect::>(),
+ TaskSelection::All => state
+ .tasks
+ .iter()
+ .filter(|(_, task)| {
+ matches!(
+ task.status,
+ TaskStatus::Queued { .. } | TaskStatus::Locked { .. }
+ )
+ })
+ .map(|(task_id, _)| *task_id)
+ .collect::>(),
+ };
+
+ for task_id in &selected_task_ids {
+ // We just checked that they're there and the state is locked. It's safe to unwrap.
+ let task = state.tasks.get_mut(task_id).expect("Task should be there.");
+
+ task.status = TaskStatus::Stashed {
+ enqueue_at: message.enqueue_at,
+ };
}
- compile_task_response("Tasks are stashed", filtered_tasks)
+ // Construct a response depending on the selected tasks.
+ if let Some(enqueue_at) = &message.enqueue_at {
+ let enqueue_at = format_datetime(settings, enqueue_at);
+
+ match &message.tasks {
+ TaskSelection::TaskIds(task_ids) => task_action_response_helper(
+ &format!("Stashed tasks will be enqueued at {enqueue_at}"),
+ task_ids.clone(),
+ |task| {
+ matches!(
+ task.status,
+ TaskStatus::Stashed { .. } | TaskStatus::Locked { .. }
+ )
+ },
+ &state,
+ ),
+ TaskSelection::Group(group) => {
+ success_msg!("Enqueue stashed tasks of group {group} at {enqueue_at}.",)
+ }
+ TaskSelection::All => {
+ success_msg!("Enqueue all stashed tasks at {enqueue_at}.",)
+ }
+ }
+ } else {
+ match &message.tasks {
+ TaskSelection::TaskIds(task_ids) => task_action_response_helper(
+ "Stashed tasks have been enqueued",
+ task_ids.clone(),
+ |task| {
+ matches!(
+ task.status,
+ TaskStatus::Stashed { .. } | TaskStatus::Locked { .. }
+ )
+ },
+ &state,
+ ),
+ TaskSelection::Group(group) => {
+ success_msg!("All stashed tasks of group \"{group}\" have been enqueued.")
+ }
+ TaskSelection::All => {
+ success_msg!("All stashed tasks have been enqueued.")
+ }
+ }
+ }
}
diff --git a/pueue/tests/daemon/integration/remove.rs b/pueue/tests/daemon/integration/remove.rs
index aa9979a0..6b4e2769 100644
--- a/pueue/tests/daemon/integration/remove.rs
+++ b/pueue/tests/daemon/integration/remove.rs
@@ -31,7 +31,14 @@ async fn test_normal_remove() -> Result<()> {
pause_tasks(shared, TaskSelection::TaskIds(vec![3])).await?;
// Stash task 5
- send_message(shared, Message::Stash(vec![5])).await?;
+ send_message(
+ shared,
+ StashMessage {
+ tasks: TaskSelection::TaskIds(vec![5]),
+ enqueue_at: None,
+ },
+ )
+ .await?;
let remove_message = Message::Remove(vec![0, 1, 2, 3, 4, 5]);
send_message(shared, remove_message).await?;
diff --git a/pueue/tests/daemon/integration/stashed.rs b/pueue/tests/daemon/integration/stashed.rs
index abf85d97..beee041a 100644
--- a/pueue/tests/daemon/integration/stashed.rs
+++ b/pueue/tests/daemon/integration/stashed.rs
@@ -57,7 +57,7 @@ async fn test_enqueued_tasks(
// Manually enqueue the task
let enqueue_message = EnqueueMessage {
- task_ids: vec![0],
+ tasks: TaskSelection::TaskIds(vec![0]),
enqueue_at: None,
};
send_message(shared, enqueue_message)
@@ -110,9 +110,15 @@ async fn test_stash_queued_task() -> Result<()> {
add_task(shared, "sleep 10").await?;
// Stash the task
- send_message(shared, Message::Stash(vec![0]))
- .await
- .context("Failed to send STash message")?;
+ send_message(
+ shared,
+ StashMessage {
+ tasks: TaskSelection::TaskIds(vec![0]),
+ enqueue_at: None,
+ },
+ )
+ .await
+ .context("Failed to send STash message")?;
let task = get_task(shared, 0).await?;
assert_eq!(task.status, TaskStatus::Stashed { enqueue_at: None });
diff --git a/pueue_lib/src/network/message.rs b/pueue_lib/src/network/message.rs
index eb828839..20ba1d2e 100644
--- a/pueue_lib/src/network/message.rs
+++ b/pueue_lib/src/network/message.rs
@@ -44,7 +44,7 @@ pub enum Message {
Add(AddMessage),
Remove(Vec),
Switch(SwitchMessage),
- Stash(Vec),
+ Stash(StashMessage),
Enqueue(EnqueueMessage),
Start(StartMessage),
@@ -149,9 +149,17 @@ pub struct SwitchMessage {
impl_into_message!(SwitchMessage, Message::Switch);
+#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
+pub struct StashMessage {
+ pub tasks: TaskSelection,
+ pub enqueue_at: Option>,
+}
+
+impl_into_message!(StashMessage, Message::Stash);
+
#[derive(PartialEq, Eq, Clone, Debug, Deserialize, Serialize)]
pub struct EnqueueMessage {
- pub task_ids: Vec,
+ pub tasks: TaskSelection,
pub enqueue_at: Option>,
}