Skip to main content

cortex_runtime/cli/
repl_progress.rs

1// Copyright 2026 Cortex Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Animated progress display for the interactive REPL mapping operation.
5//!
6//! Uses `indicatif` to show layered acquisition progress with spinners
7//! and completion markers.
8
9use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
10use std::time::Duration;
11
12/// Acquisition layer names and their display labels.
13const LAYERS: &[&str] = &[
14    "Sitemap discovery",
15    "HTTP extraction",
16    "Pattern engine",
17    "API discovery",
18    "Browser fallback",
19];
20
21/// Create a multi-step progress display for a mapping operation.
22///
23/// Returns the `MultiProgress` handle and a vector of individual progress bars.
24pub fn create_mapping_progress() -> (MultiProgress, Vec<ProgressBar>) {
25    let mp = MultiProgress::new();
26    let mut bars = Vec::with_capacity(LAYERS.len());
27
28    let spinner_style = ProgressStyle::with_template("  {spinner:.cyan} {msg}")
29        .unwrap()
30        .tick_chars("\u{25b8}\u{25b9}\u{25b8}\u{25b9}\u{25b8}");
31
32    for &layer in LAYERS {
33        let bar = mp.add(ProgressBar::new_spinner());
34        bar.set_style(spinner_style.clone());
35        bar.set_message(format!("{layer:<22} \x1b[2mwaiting\x1b[0m"));
36        bar.enable_steady_tick(Duration::from_millis(120));
37        bars.push(bar);
38    }
39
40    (mp, bars)
41}
42
43/// Mark a layer as active (currently running).
44pub fn set_layer_active(bar: &ProgressBar, layer_name: &str, detail: &str) {
45    let active_style = ProgressStyle::with_template("  {spinner:.blue} {msg}")
46        .unwrap()
47        .tick_chars("\u{2588}\u{2589}\u{258a}\u{258b}\u{258c}\u{258d}\u{258e}\u{258f} ");
48    bar.set_style(active_style);
49    bar.set_message(format!("{layer_name:<22} \x1b[34m{detail}\x1b[0m"));
50}
51
52/// Mark a layer as complete.
53pub fn set_layer_done(bar: &ProgressBar, layer_name: &str, detail: &str) {
54    bar.set_style(ProgressStyle::with_template("  {msg}").unwrap());
55    bar.set_message(format!(
56        "\x1b[32m\u{2713}\x1b[0m {layer_name:<22} \x1b[32m{detail}\x1b[0m"
57    ));
58    bar.finish();
59}
60
61/// Mark a layer as skipped.
62pub fn set_layer_skipped(bar: &ProgressBar, layer_name: &str, reason: &str) {
63    bar.set_style(ProgressStyle::with_template("  {msg}").unwrap());
64    bar.set_message(format!(
65        "\x1b[2m\u{25cb}\x1b[0m {layer_name:<22} \x1b[2m{reason}\x1b[0m"
66    ));
67    bar.finish();
68}
69
70/// Create a simple spinner for general operations.
71pub fn create_spinner(message: &str) -> ProgressBar {
72    let bar = ProgressBar::new_spinner();
73    bar.set_style(
74        ProgressStyle::with_template("  {spinner:.cyan} {msg}")
75            .unwrap()
76            .tick_chars("\u{25b8}\u{25b9}\u{25b8}\u{25b9}\u{25b8}"),
77    );
78    bar.set_message(message.to_string());
79    bar.enable_steady_tick(Duration::from_millis(120));
80    bar
81}