Challenge finished.
This commit is contained in:
parent
b4735b5807
commit
a9891242d1
261
Cargo.lock
generated
Normal file
261
Cargo.lock
generated
Normal file
@ -0,0 +1,261 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "satellite"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
@ -1,13 +1,14 @@
|
||||
[package]
|
||||
name = "satellite"
|
||||
version = "0.0.0"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
description = "Rust challenge"
|
||||
description = "Rust Challenge"
|
||||
repository = "/myrddin/satellite"
|
||||
authors = ["CyberMages LLC <Software@CyberMagesLLC.com>", "Jason Travis Smith <Myrddin@CyberMages.tech>"]
|
||||
authors = ["Jason Travis Smith <Myrddin@CyberMages.tech>"]
|
||||
readme = "README.md"
|
||||
license-file = "LICENSE.md"
|
||||
|
||||
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.44.1", features = ["macros", "rt-multi-thread", "io-std", "io-util", "time", "signal", "sync"] }
|
||||
|
89
README.md
89
README.md
@ -1,3 +1,88 @@
|
||||
# satellite
|
||||
# Satellite Propulsion System
|
||||
|
||||
Rust challenge
|
||||
## Approach
|
||||
This program will be using standard I/O for getting data.
|
||||
|
||||
Tackling this problem requires the use of asynchronous code. This is because
|
||||
the user will constantly be giving input on the command line yet time needs
|
||||
to keep moving forward to fire any propulsion commands.
|
||||
|
||||
To handle this the program uses two separate tasks. One for handling
|
||||
the input and one for running the commands given. The input, coming from
|
||||
standard I/O, will run on a separate thread because it will use blocking
|
||||
functions. The command processing can be handled in an asynchronous
|
||||
task since it can use completely asynchronous functions.
|
||||
|
||||
The two tasks are linked with message passing channels. One channel is for
|
||||
passing commands from the input task to the command processing task. The
|
||||
other channel is a watch channel on a boolean value that the input task
|
||||
will flip from true to false. This will designate that the program should
|
||||
no longer be running and needs to terminate.
|
||||
|
||||
The input reading task is the main driver of the program. It will loop until
|
||||
the program is told to stop running. This is done by the user typing "exit",
|
||||
"stop", or "quit". It will then trigger termination through the watch
|
||||
channel. If the user types a whole number between 0 and i32::MAX, it
|
||||
will create and send a propulsion command with a delay time of the given
|
||||
number, as seconds, and send it over the command channel. If the user types
|
||||
a '-1', it will create a cancel command and send it over the command
|
||||
channel. Cancel is handled as a separate command because it is a different
|
||||
action and it is clearer to handle it separately.
|
||||
|
||||
The command processing task will loop until the termination channel tells
|
||||
it to end. It will handle two async jobs. The first is processing
|
||||
commands from the command channel, either creating a propulsion interval
|
||||
countdown or clearing any that are already running. The second job is
|
||||
to run the current propulsion firing interval until it completes one firing.
|
||||
|
||||
|
||||
|
||||
## Instructions
|
||||
### Build
|
||||
To build the program you must have rustc and cargo installed.
|
||||
The instructions to do so can be found
|
||||
[here](https://www.rust-lang.org/tools/install).
|
||||
|
||||
Then to build the program navigate to the directory and run the
|
||||
following command.
|
||||
```
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
If you would like to include debugging symbols then you can run the
|
||||
following command.
|
||||
```
|
||||
cargo build
|
||||
```
|
||||
|
||||
This will pull all the required dependencies from crates.io.
|
||||
Below is the list of dependencies; this can be verified in Cargo.toml.
|
||||
* tokio
|
||||
|
||||
|
||||
### Run
|
||||
To run the program type the following command.
|
||||
```
|
||||
cargo run --release
|
||||
```
|
||||
|
||||
If you want to run a version with all the debugging symbols then run the
|
||||
following command.
|
||||
```
|
||||
cargo run
|
||||
```
|
||||
|
||||
You can also run the currently built version from the 'target' subdirectory.
|
||||
Debug builds can be found in target/debug.
|
||||
Release builds can be found in target/release.
|
||||
The executable's name is satellite.
|
||||
|
||||
### Operation
|
||||
Once the program is running it will expect input commands from the user.
|
||||
The commands can either be:
|
||||
* A whole number - To specify the amount of delay, in seconds, before the
|
||||
next time the satellite will fire its engine.
|
||||
* The number '-1' - To specify that all current propulsion commands should
|
||||
be canceled.
|
||||
* The any one of the case sensitive words
|
||||
"quit", "stop", or "exit" - To specify that the program should terminate.
|
||||
|
285
src/main.rs
285
src/main.rs
@ -1,20 +1,283 @@
|
||||
//! Rust challenge
|
||||
//! Rust Challenge
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::sync::{mpsc, watch};
|
||||
use tokio::time::{Duration, Interval};
|
||||
|
||||
mod project;
|
||||
/// Just an easier to read type.
|
||||
type IoResult<T> = Result<T, std::io::Error>;
|
||||
|
||||
|
||||
|
||||
/// Print the version of the project.
|
||||
fn print_version()
|
||||
/// The commands available to send to the Satellite.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Command
|
||||
{
|
||||
println!("{} v{}", project::get_name(), project::get_version());
|
||||
/// Fire the engine in T minus x seconds!
|
||||
Propulsion
|
||||
{
|
||||
/// The amount of seconds to wait before we fire the engines.
|
||||
delay: Duration
|
||||
},
|
||||
|
||||
/// Cancel any waiting commands.
|
||||
Cancel
|
||||
}
|
||||
|
||||
|
||||
/// The usual starting point of your project.
|
||||
fn main()
|
||||
/// The state of the program.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ProgramState
|
||||
{
|
||||
print_version();
|
||||
/// The program is currently running.
|
||||
Running,
|
||||
|
||||
/// The program has switched to shutting down.
|
||||
Terminated
|
||||
}
|
||||
|
||||
/// Reads input from the user until the termination signal is
|
||||
/// received. It will send command messages every time it reads valid input.
|
||||
fn read_user_input(command_sender: mpsc::Sender<Command>,
|
||||
term_sender: watch::Sender<ProgramState>)
|
||||
-> IoResult<()>
|
||||
{
|
||||
let mut running: bool = true;
|
||||
let mut buffer = String::new();
|
||||
|
||||
// Loop and read the user input.
|
||||
while running
|
||||
{
|
||||
// Clearing the last read line and read a new one.
|
||||
buffer.clear();
|
||||
std::io::stdin().read_line(&mut buffer)?;
|
||||
|
||||
let input = buffer.trim();
|
||||
|
||||
match input
|
||||
{
|
||||
// A termination method wasn't specified in the document, so
|
||||
// this will terminate when either stop, exit, or quit is entered.
|
||||
"exit" | "stop" | "quit" =>
|
||||
{
|
||||
println!("Exiting program.");
|
||||
running = false;
|
||||
term_sender.send_replace(ProgramState::Terminated);
|
||||
}
|
||||
|
||||
_ =>
|
||||
{
|
||||
// Check to see if we were given a number.
|
||||
match input.parse::<i32>()
|
||||
{
|
||||
Ok(seconds) =>
|
||||
{
|
||||
// Here we are just handling the different possible numbers
|
||||
// passed in. -1 cancels commands, less than -1 is ignored
|
||||
// with a message, and greated than -1 is turned into a
|
||||
// propulsion command.
|
||||
if seconds == -1
|
||||
{
|
||||
match command_sender.blocking_send(Command::Cancel)
|
||||
{
|
||||
Ok(_) =>
|
||||
{}
|
||||
Err(e) => eprintln!("Failed to send command: {}", e)
|
||||
}
|
||||
}
|
||||
else if seconds < -1
|
||||
{
|
||||
println!("All propulsion delay times are given in \
|
||||
seconds within the range of [{}, {}]",
|
||||
0,
|
||||
i32::MAX);
|
||||
println!("A value of -1 will cancel any current \
|
||||
propulsion commands.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Time to create the Propulsion command.
|
||||
let delay_secs: u64 = seconds as u64;
|
||||
let delay_duration: Duration =
|
||||
Duration::from_secs(delay_secs);
|
||||
match command_sender.try_send(Command::Propulsion {
|
||||
delay: delay_duration,
|
||||
}) {
|
||||
Ok(_) => {}
|
||||
Err(e) => eprintln!("Failed to send command: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) =>
|
||||
{
|
||||
println!("Unable to parse the given input into seconds.");
|
||||
println!("{:?}\n", e);
|
||||
println!("Please specify only the desired whole seconds \
|
||||
until the time to fire the engines.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Determines if there is an interval that needs to be run or if
|
||||
/// there is nothing to really do.
|
||||
///
|
||||
/// This was the trickiest part due to the syntax.
|
||||
fn maybe_tick<'a>(interval: Option<&'a mut Interval>)
|
||||
-> Pin<Box<dyn Future<Output = ()> + Send + 'a>>
|
||||
{
|
||||
match interval
|
||||
{
|
||||
Some(interval) =>
|
||||
{
|
||||
Box::pin(async move {
|
||||
interval.tick().await;
|
||||
()
|
||||
})
|
||||
}
|
||||
|
||||
None => Box::pin(std::future::pending())
|
||||
}
|
||||
}
|
||||
|
||||
/// The command task.
|
||||
///
|
||||
/// Processes the commands sent and fire the engine if a given
|
||||
/// propulsion command completes.
|
||||
async fn process_commands(mut command_receiver: mpsc::Receiver<Command>,
|
||||
mut term_receiver: watch::Receiver<ProgramState>)
|
||||
-> IoResult<()>
|
||||
{
|
||||
let mut stdout = tokio::io::stdout();
|
||||
let mut propulsion_interval: Option<Interval> = None;
|
||||
let mut running: bool = true;
|
||||
|
||||
while running
|
||||
{
|
||||
tokio::select! {
|
||||
// Process commands.
|
||||
Some(command) = command_receiver.recv() =>
|
||||
{
|
||||
match command
|
||||
{
|
||||
Command::Cancel =>
|
||||
{
|
||||
// Dump any interval that is running.
|
||||
propulsion_interval = None;
|
||||
}
|
||||
|
||||
Command::Propulsion { delay } =>
|
||||
{
|
||||
// Overwrite any interval with this great new interval.
|
||||
propulsion_interval = Some(tokio::time::interval(delay));
|
||||
|
||||
// Skip the first immediate tick.
|
||||
if let Some(interval) = propulsion_interval.as_mut()
|
||||
{
|
||||
interval.tick().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the interval timer if there is one.
|
||||
_ = maybe_tick(propulsion_interval.as_mut()) =>
|
||||
{
|
||||
stdout.write_all(b"firing now!\n").await?;
|
||||
stdout.flush().await?;
|
||||
|
||||
// Clear the command out since it is done.
|
||||
propulsion_interval = None;
|
||||
}
|
||||
|
||||
// Listen for termination.
|
||||
_ = term_receiver.changed() =>
|
||||
{
|
||||
running = match *term_receiver.borrow_and_update()
|
||||
{
|
||||
ProgramState::Running => { true }
|
||||
ProgramState::Terminated => { false }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Program entry point.
|
||||
#[tokio::main]
|
||||
async fn main() -> IoResult<()>
|
||||
{
|
||||
// The channel that will be used to send satellite commands between tasks.
|
||||
let (command_sender, command_receiver) = mpsc::channel::<Command>(100);
|
||||
|
||||
// The channel that will be used to signal termination of the program.
|
||||
//
|
||||
// This could be done as a satellite command since the program is driven by
|
||||
// user interaction and it all happens from the input_task, but this is a
|
||||
// program signal so I prefer to keep it seperate from the command messages.
|
||||
// It also allows for moving termination control to another task, say from
|
||||
// Ctrl+C, if the program was changed to use TCP or something else.
|
||||
let (term_sender, term_receiver) = watch::channel(ProgramState::Running);
|
||||
|
||||
// Spawn a task to handle reading input from the user. We use a thread here
|
||||
// because tokio recommends it for the blocking read calls for interactive
|
||||
// user input.
|
||||
let input_task = tokio::task::spawn_blocking(move || {
|
||||
read_user_input(command_sender, term_sender)
|
||||
});
|
||||
|
||||
// Spawn a task to handle simulating the satellite.
|
||||
let command_task = tokio::spawn(async move {
|
||||
process_commands(command_receiver, term_receiver).await
|
||||
});
|
||||
|
||||
// Run both tasks at the same time.
|
||||
let (command_result, input_result) = tokio::join!(command_task, input_task);
|
||||
|
||||
// Handle errors from the tasks.
|
||||
match command_result
|
||||
{
|
||||
Ok(Ok(())) =>
|
||||
{}
|
||||
|
||||
Ok(Err(e)) =>
|
||||
{
|
||||
eprintln!("Command task failed: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Err(e) =>
|
||||
{
|
||||
eprintln!("Command task panicked or was aborted: {}", e);
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
|
||||
}
|
||||
}
|
||||
|
||||
match input_result
|
||||
{
|
||||
Ok(Ok(())) =>
|
||||
{}
|
||||
|
||||
Ok(Err(e)) =>
|
||||
{
|
||||
eprintln!("Input task failed: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Err(e) =>
|
||||
{
|
||||
eprintln!("Input task panicked or was aborted: {}", e);
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
|
||||
}
|
||||
}
|
||||
|
||||
println!("Shutdown complete.");
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user