Skip to content

Commit

Permalink
Merge pull request #37 from edouardpoitras/chaining-commands
Browse files Browse the repository at this point in the history
Implemented new Chain component and ChainCompletedEvent
  • Loading branch information
edouardpoitras authored Aug 23, 2024
2 parents 4bfa855 + f82c059 commit 05525ae
Show file tree
Hide file tree
Showing 9 changed files with 386 additions and 7 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,23 @@ fn delay_process_start(mut commands: Commands) {
}
```

**Chaining:**

```rust
fn chain_multiple_commands(mut commands: Commands) {
commands.spawn((
Chain::new(vec![
LocalCommand::new("sh").args(["-c", "echo 'First command'"]),
LocalCommand::new("sh").args(["-c", "echo 'Second command'"]),
LocalCommand::new("sh").args(["-c", "echo 'Third command'"]),
]),
Retry::Attempts(2), // Retry applies to any link in the chain
Delay::Fixed(Duration::from_secs(3)), // Wait 3s between retries and chain commands
Cleanup::RemoveComponents // Remove Chain, Retry, Delay, and Cleanup components upon completion
));
}
```

## Todo

- [ ] Mac testing (not sure if it works yet)
Expand Down
62 changes: 62 additions & 0 deletions examples/chain_failure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use bevy::prelude::*;
use bevy_local_commands::{
BevyLocalCommandsPlugin, Chain, LocalCommand, ProcessCompleted, ProcessOutput,
};

fn main() {
App::new()
.add_plugins((MinimalPlugins, BevyLocalCommandsPlugin))
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}

fn setup(mut commands: Commands) {
// Create a chain of commands
#[cfg(not(windows))]
let chain = Chain::new(vec![
LocalCommand::new("sh").args(["-c", "echo 'First command' && sleep 1"]),
LocalCommand::new("commanddoesnotexist").args(["this should fail"]), // Failure
LocalCommand::new("sh").args(["-c", "echo 'Third command' && sleep 1"]),
// Same result with a failed running command
//LocalCommand::new("sh").args(["-c", "exit 1"]), // Failure
]);
#[cfg(windows)]
let chain = Chain::new(vec![
LocalCommand::new("powershell").args(["echo 'First command' && sleep 1"]),
LocalCommand::new("commanddoesnotexist").args(["this should fail"]), // Failure
LocalCommand::new("powershell").args(["echo 'Third command' && sleep 1"]),
// Same result with a failed running command
//LocalCommand::new("powershell").args(["exit 1"]), // Failure
]);

// Spawn an entity with the Chain component
let id = commands.spawn(chain).id();
println!("Spawned the chain as entity {id:?}");
}

fn update(
mut process_output_event: EventReader<ProcessOutput>,
mut process_completed_event: EventReader<ProcessCompleted>,
chain_query: Query<(), With<Chain>>,
) {
for process_output in process_output_event.read() {
for line in process_output.lines() {
println!("Output Line ({:?}): {line}", process_output.entity);
}
}

for process_completed in process_completed_event.read() {
println!(
"Command {:?} completed (Success - {})",
process_completed.entity,
process_completed.exit_status.success()
);
}

// Check if there are no more Chain components (all chains completed)
if chain_query.is_empty() {
println!("Chain commands done. Exiting the app.");
std::process::exit(0);
}
}
81 changes: 81 additions & 0 deletions examples/chain_failure_delay_retries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::time::Duration;

use bevy::prelude::*;
use bevy_local_commands::{
BevyLocalCommandsPlugin, Chain, ChainCompletedEvent, Delay, LocalCommand, ProcessCompleted,
ProcessOutput, Retry, RetryEvent,
};

fn main() {
App::new()
.add_plugins((MinimalPlugins, BevyLocalCommandsPlugin))
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}

fn setup(mut commands: Commands) {
// Spawn an entity with the relevant components
#[cfg(not(windows))]
let chain = Chain::new(vec![
LocalCommand::new("sh").args(["-c", "echo 'First command'"]),
LocalCommand::new("sh").args(["-c", "exit 1"]), // Failure
LocalCommand::new("sh").args(["-c", "echo 'Third command'"]),
]);
#[cfg(windows)]
let chain = Chain::new(vec![
LocalCommand::new("powershell").args(["echo 'First command'"]),
LocalCommand::new("powershell").args(["exit 1"]), // Failure
LocalCommand::new("powershell").args(["echo 'Third command'"]),
]);
let id = commands
.spawn((
chain,
Retry::Attempts(2),
Delay::Fixed(Duration::from_secs(2)),
))
.id();
println!("Spawned the chain as entity {id:?}");
}

fn update(
mut process_output_event: EventReader<ProcessOutput>,
mut process_completed_event: EventReader<ProcessCompleted>,
mut process_chain_completed_event: EventReader<ChainCompletedEvent>,
mut process_retry_event: EventReader<RetryEvent>,
chain_query: Query<(), With<Chain>>,
) {
for process_output in process_output_event.read() {
for line in process_output.lines() {
println!("Output Line ({:?}): {line}", process_output.entity);
}
}

for process_retry in process_retry_event.read() {
println!(
"Command for entity {:?} failed, retrying ({} left)",
process_retry.entity, process_retry.retries_left,
);
}

for process_completed in process_completed_event.read() {
println!(
"Command {:?} completed (Success - {})",
process_completed.entity,
process_completed.exit_status.success()
);
}

for process_chain_completed in process_chain_completed_event.read() {
println!(
"Chain of commands completed for entity {} (Success - {})",
process_chain_completed.entity, process_chain_completed.success,
);
}

// Check if there is no more Chain component
if chain_query.is_empty() {
println!("Chain commands done. Exiting the app.");
std::process::exit(0);
}
}
66 changes: 66 additions & 0 deletions examples/chain_retries_delay_cleanup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::time::Duration;

use bevy::prelude::*;
use bevy_local_commands::{
BevyLocalCommandsPlugin, Chain, Cleanup, Delay, LocalCommand, ProcessCompleted, ProcessOutput,
Retry,
};

fn main() {
App::new()
.add_plugins((MinimalPlugins, BevyLocalCommandsPlugin))
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}

fn setup(mut commands: Commands) {
// Spawn a entity with all addons
#[cfg(not(windows))]
let chain = Chain::new(vec![
LocalCommand::new("sh").args(["-c", "echo 'First command'"]),
LocalCommand::new("sh").args(["-c", "echo 'Second command'"]),
LocalCommand::new("sh").args(["-c", "echo 'Third command'"]),
]);
#[cfg(windows)]
let chain = Chain::new(vec![
LocalCommand::new("powershell").args(["echo 'First command'"]),
LocalCommand::new("powershell").args(["echo 'Second command'"]),
LocalCommand::new("powershell").args(["echo 'Third command'"]),
]);
let id = commands
.spawn((
chain,
Retry::Attempts(2),
Delay::Fixed(Duration::from_secs(3)),
Cleanup::RemoveComponents,
))
.id();
println!("Spawned as entity {id:?}");
}

fn update(
mut process_output_event: EventReader<ProcessOutput>,
mut process_completed_event: EventReader<ProcessCompleted>,
command_query: Query<(), (With<Chain>, With<Retry>, With<Delay>, With<Cleanup>)>,
) {
for process_output in process_output_event.read() {
for line in process_output.lines() {
println!("Output Line ({:?}): {line}", process_output.entity);
}
}

for process_completed in process_completed_event.read() {
println!(
"Command {:?} completed (Success - {})",
process_completed.entity,
process_completed.exit_status.success()
);
}

// Check if our entity is done running chain commands
if command_query.is_empty() {
println!("All commands completed - cleanup performed. Exiting the app.");
std::process::exit(0);
}
}
6 changes: 5 additions & 1 deletion examples/run_all_examples.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
cargo run --example chain_failure
cargo run --example chain_failure_delay_retries
cargo run --example chain_retries_delay_cleanup
cargo run --example despawn_on_completion
cargo run --example error
cargo run --example input
cargo run --example kill
cargo run --example retries_and_delay_and_cleanup
cargo run --example retries_and_delay
cargo run --example retries_and_remove
cargo run --example simple
cargo run --example simple
cargo run --example simple_chain
58 changes: 58 additions & 0 deletions examples/simple_chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use bevy::prelude::*;
use bevy_local_commands::{
BevyLocalCommandsPlugin, Chain, LocalCommand, ProcessCompleted, ProcessOutput,
};

fn main() {
App::new()
.add_plugins((MinimalPlugins, BevyLocalCommandsPlugin))
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}

fn setup(mut commands: Commands) {
// Create a chain of commands
#[cfg(not(windows))]
let chain = Chain::new(vec![
LocalCommand::new("sh").args(["-c", "echo 'First command' && sleep 1"]),
LocalCommand::new("sh").args(["-c", "echo 'Second command' && sleep 1"]),
LocalCommand::new("sh").args(["-c", "echo 'Third command' && sleep 1"]),
]);
#[cfg(windows)]
let chain = Chain::new(vec![
LocalCommand::new("powershell").args(["echo 'First command' && sleep 1"]),
LocalCommand::new("powershell").args(["echo 'Second command' && sleep 1"]),
LocalCommand::new("powershell").args(["echo 'Third command' && sleep 1"]),
]);

// Spawn an entity with the Chain component
let id = commands.spawn(chain).id();
println!("Spawned the chain as entity {id:?}");
}

fn update(
mut process_output_event: EventReader<ProcessOutput>,
mut process_completed_event: EventReader<ProcessCompleted>,
chain_query: Query<(), With<Chain>>,
) {
for process_output in process_output_event.read() {
for line in process_output.lines() {
println!("Output Line ({:?}): {line}", process_output.entity);
}
}

for process_completed in process_completed_event.read() {
println!(
"Command {:?} completed (Success - {})",
process_completed.entity,
process_completed.exit_status.success()
);
}

// Check if there are no more Chain components (all chains completed)
if chain_query.is_empty() {
println!("All chain commands completed. Exiting the app.");
std::process::exit(0);
}
}
Loading

0 comments on commit 05525ae

Please sign in to comment.