use std::cell::RefCell;
use std::rc::Rc;
use std::time::{Duration, Instant};
use runite::time::sleep;
#[derive(Clone, Copy, PartialEq)]
enum JobState {
Running,
Done(Duration),
}
struct Job {
name: String,
started: Instant,
state: JobState,
}
#[derive(Default)]
struct JobBoard {
jobs: Vec<Job>,
}
impl JobBoard {
fn print_status(&self) {
if self.jobs.is_empty() {
println!(" (no jobs yet — try `job build 800`)");
return;
}
for (id, job) in self.jobs.iter().enumerate() {
match job.state {
JobState::Running => {
println!(
" #{id} {:<12} running ({:?})",
job.name,
job.started.elapsed()
)
}
JobState::Done(took) => println!(" #{id} {:<12} done in {took:?}", job.name),
}
}
}
fn unfinished(&self) -> Vec<&str> {
self.jobs
.iter()
.filter(|job| job.state == JobState::Running)
.map(|job| job.name.as_str())
.collect()
}
}
fn start_job(board: &Rc<RefCell<JobBoard>>, name: String, duration: Duration) {
let id = {
let mut board = board.borrow_mut();
board.jobs.push(Job {
name: name.clone(),
started: Instant::now(),
state: JobState::Running,
});
board.jobs.len() - 1
};
println!("started job #{id} `{name}` ({duration:?})");
let board = Rc::clone(board);
runite::spawn(async move {
sleep(duration).await;
let mut board = board.borrow_mut();
let took = board.jobs[id].started.elapsed();
board.jobs[id].state = JobState::Done(took);
println!(
"\u{2713} job #{id} `{}` finished in {took:?}",
board.jobs[id].name
);
});
}
fn dispatch(board: &Rc<RefCell<JobBoard>>, line: &str) -> bool {
let mut words = line.split_whitespace();
match words.next() {
Some("job") => {
let name = words.next().unwrap_or("work").to_string();
let millis = words.next().and_then(|w| w.parse().ok()).unwrap_or(1000u64);
start_job(board, name, Duration::from_millis(millis));
}
Some("status") => board.borrow().print_status(),
Some("help") => {
println!(" job <name> <millis> start a background job");
println!(" status show the job board");
println!(" quit exit");
}
Some("quit") => return false,
Some(other) => println!("unknown command {other:?} (try `help`)"),
None => {}
}
true
}
async fn interactive(board: Rc<RefCell<JobBoard>>) -> std::io::Result<()> {
println!("command center — type `help` for commands");
let mut input = runite::stdin()?;
while let Some(line) = input.read_line().await? {
if !dispatch(&board, line.trim()) {
break;
}
}
Ok(())
}
async fn demo(board: Rc<RefCell<JobBoard>>) {
let script: &[(&str, u64)] = &[
("job compile 300", 50),
("job test 150", 50),
("status", 250),
("status", 200),
("job deploy 5000", 50),
("quit", 0),
];
for (line, pause_after) in script {
println!("> {line}");
if !dispatch(&board, line) {
break;
}
sleep(Duration::from_millis(*pause_after)).await;
}
}
#[runite::main]
async fn main() -> std::io::Result<()> {
let board = Rc::new(RefCell::new(JobBoard::default()));
if std::env::args().any(|arg| arg == "--demo") {
demo(Rc::clone(&board)).await;
} else {
interactive(Rc::clone(&board)).await?;
}
let unfinished = board.borrow().unfinished().join(", ");
if unfinished.is_empty() {
println!("goodbye — all jobs complete");
} else {
println!("goodbye — abandoning unfinished jobs: {unfinished}");
}
Ok(())
}