pub mod chart_updater;
pub mod color_coder;
pub mod delta_updater;
pub mod linker;
pub mod table_updater;
pub mod verbose;
use std::time::{Duration, Instant};
use console::Style;
use indicatif::{ProgressBar, ProgressStyle};
use crate::cli::resolve_steps;
use crate::com::dispatch::Dispatch;
use crate::config::Config;
use crate::error::OaResult;
use crate::shapes::inventory::SlideInventory;
#[derive(Debug, Clone)]
pub struct StepResult {
pub name: &'static str,
pub count: usize,
pub elapsed_secs: f64,
pub ok: bool,
}
#[derive(Debug, Default)]
pub struct PipelineResults {
pub steps: Vec<StepResult>,
pub links_updated: usize,
pub tables_updated: usize,
pub deltas_updated: usize,
pub tables_colored: usize,
pub charts_updated: usize,
}
impl PipelineResults {
pub fn total_objects(&self) -> usize {
self.steps.iter().map(|s| s.count).sum()
}
}
fn s_ok() -> Style { Style::new().green() }
fn s_count() -> Style { Style::new().white().bold() }
fn s_dim() -> Style { Style::new().dim() }
const STEP_WIDTH: usize = 30;
pub fn format_step_line_pub(name: &str, count: usize, secs: f64) -> String {
let leader_len = STEP_WIDTH.saturating_sub(name.len() + 1);
let leaders = "·".repeat(leader_len);
format!(
" {} {} {} {:>4} {}",
s_ok().apply_to("•"),
name,
s_dim().apply_to(&leaders),
s_count().apply_to(count),
s_dim().apply_to(format!("{secs:.1}s")),
)
}
pub fn make_spinner_pub(step_name: &str) -> ProgressBar { make_spinner(step_name) }
fn make_spinner(step_name: &str) -> ProgressBar {
let leader_len = STEP_WIDTH.saturating_sub(step_name.len() + 1);
let leaders = "·".repeat(leader_len);
let msg = format!("{step_name} {leaders}");
let pb = ProgressBar::new_spinner();
pb.set_style(
ProgressStyle::default_spinner()
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"])
.template(" {spinner:.cyan} {msg}")
.unwrap()
);
pb.set_message(msg);
pb.enable_steady_tick(Duration::from_millis(80));
pb
}
macro_rules! run_step {
($results:expr, $quiet:expr, $name:expr, $field:ident, $body:expr) => {{
let use_spinner = !$quiet && !verbose::is_verbose();
let spinner = if use_spinner { Some(make_spinner($name)) } else { None };
let t = Instant::now();
let count = $body;
$results.$field = count;
let elapsed = t.elapsed().as_secs_f64();
if let Some(pb) = spinner {
pb.finish_and_clear();
}
if !$quiet && !verbose::is_verbose() {
println!("{}", format_step_line_pub($name, count, elapsed));
}
$results.steps.push(StepResult { name: $name, count, elapsed_secs: elapsed, ok: true });
}};
}
pub fn run_pipeline(
inventory: &SlideInventory,
config: &Config,
presentation: &mut Dispatch,
excel_app: &mut Dispatch,
excel_path: &str,
steps_include: &[String],
steps_skip: &[String],
quiet: bool,
verbose: bool,
skip_chart_refresh: bool,
) -> OaResult<PipelineResults> {
verbose::set_verbose(verbose);
let active_steps = resolve_steps(steps_include, steps_skip)
.map_err(|e| crate::error::OaError::Config(e))?;
let mut results = PipelineResults::default();
if active_steps.iter().any(|s| s == "links") {
run_step!(results, quiet, "Links", links_updated,
linker::update_links(inventory, excel_path, config)?);
}
if active_steps.iter().any(|s| s == "tables") {
run_step!(results, quiet, "Tables", tables_updated,
table_updater::update_tables(inventory, config, excel_app, excel_path)?);
}
if active_steps.iter().any(|s| s == "deltas") {
run_step!(results, quiet, "Deltas", deltas_updated,
delta_updater::update_deltas(inventory, config, presentation, excel_path, excel_app)?);
}
if active_steps.iter().any(|s| s == "coloring") {
run_step!(results, quiet, "Coloring", tables_colored,
color_coder::apply_color_coding(inventory, config)?);
}
if active_steps.iter().any(|s| s == "charts") {
run_step!(results, quiet, "Charts", charts_updated,
chart_updater::update_charts(inventory, excel_path, skip_chart_refresh)?);
}
Ok(results)
}