use crate::{
commands::{Config, is_available, not_available},
database::Result,
entities::{Limit, format_job_id, format_opt},
jobber::JobberGet,
views::ApplyTableStyle,
};
use color_print::cwriteln;
use tabled::{
Table, Tabled,
settings::{Alignment, Color, Width, object::Columns, themes::Colorization},
};
#[derive(Tabled)]
pub(crate) struct JobView {
#[tabled(rename = "Pos")]
pos: String,
#[tabled(rename = "Worker")]
worker: String,
#[tabled(rename = "Client")]
client: String,
#[tabled(rename = "Subject")]
subject: String,
#[tabled(rename = "Rate")]
rate: String,
#[tabled(rename = "Interval")]
interval: String,
#[tabled(rename = "Billing")]
billing: String,
#[tabled(rename = "Work")]
work: String,
#[tabled(rename = "Revenue")]
revenue: String,
#[tabled(rename = "Current Work")]
current_work: String,
}
impl JobView {
pub(crate) fn new(
job_id: usize,
config: &Config,
database: &impl JobberGet,
ansi: bool,
) -> Result<Self> {
let job = database.get_job(job_id)?;
let scheme = &job.scheme(job_id)?;
let work = job.work(job_id)?;
Ok(Self {
pos: format_job_id(job_id, database, config, ansi)?,
worker: database.get_full_worker_name(&job.worker)?,
client: database.get_full_client_name(&job.client)?,
subject: job.subject.clone(),
rate: format_opt(&scheme.rate, ansi),
interval: scheme.format_duration(
scheme.min_work_interval.duration(),
Limit::None,
ansi,
),
billing: format_opt(&scheme.billing_period, ansi),
work: scheme.format_duration(work, Limit::None, ansi),
revenue: scheme.format_revenue(work, ansi),
current_work: if let Some(running_work) = &job.running {
format!(
"since {} ({})",
scheme.format_date_time(running_work.start),
scheme.format_duration(running_work.duration(), Limit::Day, ansi)
)
} else {
not_available(ansi)
},
})
}
}
pub(crate) struct PosIterator<T> {
total: usize,
special_pos: usize,
special_val: T,
default_val: T,
current: usize,
}
impl<T> PosIterator<T> {
fn new(total: usize, special_pos: usize, special_val: T, default_val: T) -> Self {
PosIterator {
total,
special_pos,
special_val,
default_val,
current: 0,
}
}
}
impl<T: Clone> Iterator for PosIterator<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.total {
return None;
}
let val = if self.current == self.special_pos {
self.special_val.clone()
} else {
self.default_val.clone()
};
self.current += 1;
Some(val)
}
}
pub(crate) fn create_job_table(
rows: impl IntoIterator<Item = JobView>,
current_job: &Option<usize>,
num_jobs: usize,
config: &Config,
) -> Table {
let mut table = Table::new(rows);
table.modify(Columns::first(), Alignment::right());
table.modify(Columns::one(4), Width::wrap(40));
table.modify(Columns::one(4), Alignment::right());
table.modify(Columns::one(6), Alignment::right());
table.modify(Columns::one(5), Alignment::center());
table.modify(Columns::one(7), Alignment::right());
table.modify(Columns::one(8), Alignment::right());
if let Some(current_job) = current_job {
table.with(Colorization::rows(PosIterator::new(
num_jobs + 1,
current_job + 1,
Color::BOLD,
Color::default(),
)));
}
table.style(&config.table_style);
table
}
impl std::fmt::Display for JobView {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
cwriteln!(f, " Job ID: <s>{}</>", self.pos,)?;
cwriteln!(f, " Worker: <s>{}</>", self.worker)?;
cwriteln!(f, " Client: <s>{}</>", self.client)?;
if !self.subject.is_empty() {
cwriteln!(f, " Subject: <i>{}</>", &self.subject)?;
}
if !self.work.is_empty() {
cwriteln!(f, " Rate: {}", self.rate)?;
cwriteln!(f, " Interval: {}", self.interval)?;
cwriteln!(f, " Billing: {}", self.billing)?;
if is_available(&self.work) {
cwriteln!(f, " Work: {}", self.work)?;
}
if is_available(&self.revenue) {
cwriteln!(f, " Revenue: {}", self.revenue)?;
}
}
Ok(())
}
}