use std::time::Duration;
use crate::app::Snapshot;
use crate::board::{BoardRow, Column};
use crate::spend::{Ceiling, Prices};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Facts {
pub workspace: String,
pub project: String,
pub cap: usize,
pub count: usize,
pub tick: Duration,
pub lease: Option<Duration>,
pub since_act: Option<i64>,
pub ceiling: Option<String>,
}
impl Facts {
pub fn has_room(&self) -> bool {
self.count < self.cap && self.ceiling.is_none()
}
pub fn label(&self) -> String {
let last = match self.since_act {
None => "nothing yet".to_owned(),
Some(secs) => format!("last {} ago", secs_label(secs)),
};
let lease = match self.lease {
None => "no lease".to_owned(),
Some(lease) => format!("lease {}", period_label(lease)),
};
format!(
"{}/{} drones · tick {} · {last} · {lease}",
self.count,
self.cap,
period_label(self.tick),
)
}
}
pub(crate) fn secs_label(secs: i64) -> String {
crate::nav::convs::age_label(secs)
}
fn period_label(period: Duration) -> String {
secs_label(i64::try_from(period.as_secs()).unwrap_or(i64::MAX))
}
pub fn of(
snap: &Snapshot,
prices: &Prices,
ceiling: Ceiling,
rows: &[BoardRow],
now: i64,
) -> Vec<Facts> {
let acts = super::row::of_rows(&snap.ops);
let verdict = ceiling.verdict(spent(snap, prices));
snap.fleet
.iter()
.map(|(key, policy)| {
let workspace = crate::naming::leaf(std::path::Path::new(key));
Facts {
count: held(rows, &workspace),
cap: policy.cap,
project: snap.project_name(&policy.project),
tick: snap.cadence.full_sweep,
lease: policy.lease,
since_act: super::row::last_act(&acts, key).map(|ts| now.saturating_sub(ts)),
ceiling: verdict.clone(),
workspace,
}
})
.collect()
}
pub fn held(rows: &[BoardRow], workspace: &str) -> usize {
rows.iter()
.filter(|r| r.column == Column::Claimed && r.workspace.as_deref() == Some(workspace))
.count()
}
fn spent(snap: &Snapshot, prices: &Prices) -> Option<crate::spend::Cost> {
let bills: Vec<_> = snap.bills.values().flatten().cloned().collect();
crate::spend::priced(&bills, prices)
}
#[cfg(test)]
mod tests;