fix: improve usage of indicatif for tracking progress
This commit is contained in:
parent
29a261575b
commit
403e199062
@ -10,13 +10,16 @@ use super::{
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum HarmonyEvent {
|
pub enum HarmonyEvent {
|
||||||
HarmonyStarted,
|
HarmonyStarted,
|
||||||
|
HarmonyFinished,
|
||||||
InterpretExecutionStarted {
|
InterpretExecutionStarted {
|
||||||
|
execution_id: String,
|
||||||
topology: String,
|
topology: String,
|
||||||
interpret: String,
|
interpret: String,
|
||||||
score: String,
|
score: String,
|
||||||
message: String,
|
message: String,
|
||||||
},
|
},
|
||||||
InterpretExecutionFinished {
|
InterpretExecutionFinished {
|
||||||
|
execution_id: String,
|
||||||
topology: String,
|
topology: String,
|
||||||
interpret: String,
|
interpret: String,
|
||||||
score: String,
|
score: String,
|
||||||
|
@ -5,6 +5,7 @@ use serde::Serialize;
|
|||||||
use serde_value::Value;
|
use serde_value::Value;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
data::Id,
|
||||||
instrumentation::{self, HarmonyEvent},
|
instrumentation::{self, HarmonyEvent},
|
||||||
interpret::{Interpret, InterpretError, Outcome},
|
interpret::{Interpret, InterpretError, Outcome},
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
@ -20,9 +21,11 @@ pub trait Score<T: Topology>:
|
|||||||
inventory: &Inventory,
|
inventory: &Inventory,
|
||||||
topology: &T,
|
topology: &T,
|
||||||
) -> Result<Outcome, InterpretError> {
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let id = Id::default();
|
||||||
let interpret = self.create_interpret();
|
let interpret = self.create_interpret();
|
||||||
|
|
||||||
instrumentation::instrument(HarmonyEvent::InterpretExecutionStarted {
|
instrumentation::instrument(HarmonyEvent::InterpretExecutionStarted {
|
||||||
|
execution_id: id.clone().to_string(),
|
||||||
topology: topology.name().into(),
|
topology: topology.name().into(),
|
||||||
interpret: interpret.get_name().to_string(),
|
interpret: interpret.get_name().to_string(),
|
||||||
score: self.name(),
|
score: self.name(),
|
||||||
@ -32,6 +35,7 @@ pub trait Score<T: Topology>:
|
|||||||
let result = interpret.execute(inventory, topology).await;
|
let result = interpret.execute(inventory, topology).await;
|
||||||
|
|
||||||
instrumentation::instrument(HarmonyEvent::InterpretExecutionFinished {
|
instrumentation::instrument(HarmonyEvent::InterpretExecutionFinished {
|
||||||
|
execution_id: id.clone().to_string(),
|
||||||
topology: topology.name().into(),
|
topology: topology.name().into(),
|
||||||
interpret: interpret.get_name().to_string(),
|
interpret: interpret.get_name().to_string(),
|
||||||
score: self.name(),
|
score: self.name(),
|
||||||
|
@ -7,7 +7,6 @@ use serde::Serialize;
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::HARMONY_DATA_DIR,
|
config::HARMONY_DATA_DIR,
|
||||||
data::{Id, Version},
|
data::{Id, Version},
|
||||||
instrumentation::{self, HarmonyEvent},
|
|
||||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
score::Score,
|
score::Score,
|
||||||
|
@ -2,18 +2,15 @@ use harmony::{
|
|||||||
instrumentation::{self, HarmonyEvent},
|
instrumentation::{self, HarmonyEvent},
|
||||||
topology::TopologyStatus,
|
topology::TopologyStatus,
|
||||||
};
|
};
|
||||||
use indicatif::{MultiProgress, ProgressBar};
|
use indicatif::MultiProgress;
|
||||||
use indicatif_log_bridge::LogWrapper;
|
use indicatif_log_bridge::LogWrapper;
|
||||||
use std::{
|
use std::sync::{Arc, Mutex};
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::progress;
|
use crate::progress::{IndicatifProgressTracker, ProgressTracker};
|
||||||
|
|
||||||
pub fn init() -> tokio::task::JoinHandle<()> {
|
pub fn init() -> tokio::task::JoinHandle<()> {
|
||||||
configure_logger();
|
let base_progress = configure_logger();
|
||||||
let handle = tokio::spawn(handle_events());
|
let handle = tokio::spawn(handle_events(base_progress));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if instrumentation::instrument(HarmonyEvent::HarmonyStarted).is_ok() {
|
if instrumentation::instrument(HarmonyEvent::HarmonyStarted).is_ok() {
|
||||||
@ -24,32 +21,38 @@ pub fn init() -> tokio::task::JoinHandle<()> {
|
|||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_logger() {
|
fn configure_logger() -> MultiProgress {
|
||||||
let logger =
|
let logger =
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).build();
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).build();
|
||||||
let level = logger.filter();
|
let level = logger.filter();
|
||||||
let multi = MultiProgress::new();
|
let progress = MultiProgress::new();
|
||||||
LogWrapper::new(multi.clone(), logger).try_init().unwrap();
|
|
||||||
|
LogWrapper::new(progress.clone(), logger)
|
||||||
|
.try_init()
|
||||||
|
.unwrap();
|
||||||
log::set_max_level(level);
|
log::set_max_level(level);
|
||||||
|
|
||||||
|
progress
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_events() {
|
async fn handle_events(base_progress: MultiProgress) {
|
||||||
instrumentation::subscribe("Harmony CLI Logger", {
|
let progress_tracker = Arc::new(IndicatifProgressTracker::new(base_progress.clone()));
|
||||||
let sections: Arc<Mutex<HashMap<String, MultiProgress>>> =
|
let preparing_topology = Arc::new(Mutex::new(false));
|
||||||
Arc::new(Mutex::new(HashMap::new()));
|
|
||||||
let progress_bars: Arc<Mutex<HashMap<String, ProgressBar>>> =
|
|
||||||
Arc::new(Mutex::new(HashMap::new()));
|
|
||||||
|
|
||||||
|
instrumentation::subscribe("Harmony CLI Logger", {
|
||||||
move |event| {
|
move |event| {
|
||||||
let sections_clone = Arc::clone(§ions);
|
let progress_tracker = Arc::clone(&progress_tracker);
|
||||||
let progress_bars_clone = Arc::clone(&progress_bars);
|
let preparing_topology = Arc::clone(&preparing_topology);
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let mut sections = sections_clone.lock().unwrap();
|
let mut preparing_topology = preparing_topology.lock().unwrap();
|
||||||
let mut progress_bars = progress_bars_clone.lock().unwrap();
|
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
HarmonyEvent::HarmonyStarted => {}
|
HarmonyEvent::HarmonyStarted => {}
|
||||||
|
HarmonyEvent::HarmonyFinished => {
|
||||||
|
progress_tracker.add_section("harmony-finished", "\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
HarmonyEvent::TopologyStateChanged {
|
HarmonyEvent::TopologyStateChanged {
|
||||||
topology,
|
topology,
|
||||||
status,
|
status,
|
||||||
@ -60,112 +63,82 @@ async fn handle_events() {
|
|||||||
match status {
|
match status {
|
||||||
TopologyStatus::Queued => {}
|
TopologyStatus::Queued => {}
|
||||||
TopologyStatus::Preparing => {
|
TopologyStatus::Preparing => {
|
||||||
let section = progress::new_section(format!(
|
progress_tracker.add_section(
|
||||||
"{} Preparing environment: {topology}...",
|
§ion_key,
|
||||||
crate::theme::EMOJI_TOPOLOGY,
|
&format!(
|
||||||
));
|
"\n{} Preparing environment: {topology}...",
|
||||||
(*sections).insert(section_key, section);
|
crate::theme::EMOJI_TOPOLOGY
|
||||||
|
),
|
||||||
|
);
|
||||||
|
(*preparing_topology) = true;
|
||||||
}
|
}
|
||||||
TopologyStatus::Success => {
|
TopologyStatus::Success => {
|
||||||
let section = (*sections).get(§ion_key).unwrap();
|
(*preparing_topology) = false;
|
||||||
let progress = progress::add_spinner(section, "".into());
|
progress_tracker.add_task(§ion_key, "topology-success", "");
|
||||||
|
progress_tracker
|
||||||
progress::success(
|
.finish_task("topology-success", &message.unwrap_or("".into()));
|
||||||
section,
|
|
||||||
Some(progress),
|
|
||||||
message.unwrap_or("".into()),
|
|
||||||
);
|
|
||||||
|
|
||||||
(*sections).remove(§ion_key);
|
|
||||||
}
|
}
|
||||||
TopologyStatus::Noop => {
|
TopologyStatus::Noop => {
|
||||||
let section = (*sections).get(§ion_key).unwrap();
|
(*preparing_topology) = false;
|
||||||
let progress = progress::add_spinner(section, "".into());
|
progress_tracker.add_task(§ion_key, "topology-skip", "");
|
||||||
|
progress_tracker
|
||||||
progress::skip(
|
.skip_task("topology-skip", &message.unwrap_or("".into()));
|
||||||
section,
|
|
||||||
Some(progress),
|
|
||||||
message.unwrap_or("".into()),
|
|
||||||
);
|
|
||||||
|
|
||||||
(*sections).remove(§ion_key);
|
|
||||||
}
|
}
|
||||||
TopologyStatus::Error => {
|
TopologyStatus::Error => {
|
||||||
let section = (*sections).get(§ion_key).unwrap();
|
progress_tracker.add_task(§ion_key, "topology-error", "");
|
||||||
let progress = progress::add_spinner(section, "".into());
|
(*preparing_topology) = false;
|
||||||
|
progress_tracker
|
||||||
progress::error(
|
.fail_task("topology-error", &message.unwrap_or("".into()));
|
||||||
section,
|
|
||||||
Some(progress),
|
|
||||||
message.unwrap_or("".into()),
|
|
||||||
);
|
|
||||||
|
|
||||||
(*sections).remove(§ion_key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HarmonyEvent::InterpretExecutionStarted {
|
HarmonyEvent::InterpretExecutionStarted {
|
||||||
|
execution_id: task_key,
|
||||||
topology,
|
topology,
|
||||||
interpret,
|
interpret: _,
|
||||||
score,
|
score,
|
||||||
message,
|
message,
|
||||||
} => {
|
} => {
|
||||||
let section_key = if (*sections).contains_key(&topology_key(&topology)) {
|
let section_key = if (*preparing_topology)
|
||||||
|
&& progress_tracker.contains_section(&topology_key(&topology))
|
||||||
|
{
|
||||||
topology_key(&topology)
|
topology_key(&topology)
|
||||||
} else if (*sections).contains_key(&score_key(&score)) {
|
} else if progress_tracker.contains_section(&score_key(&score)) {
|
||||||
score_key(&interpret)
|
score_key(&score)
|
||||||
} else {
|
} else {
|
||||||
let key = score_key(&score);
|
let key = score_key(&score);
|
||||||
let section = progress::new_section(format!(
|
progress_tracker.add_section(
|
||||||
"\n{} Interpreting score: {score}...",
|
&key,
|
||||||
crate::theme::EMOJI_SCORE,
|
&format!(
|
||||||
));
|
"{} Interpreting score: {score}...",
|
||||||
(*sections).insert(key.clone(), section);
|
crate::theme::EMOJI_SCORE
|
||||||
|
),
|
||||||
|
);
|
||||||
key
|
key
|
||||||
};
|
};
|
||||||
let section = (*sections).get(§ion_key).unwrap();
|
|
||||||
let progress_bar = progress::add_spinner(section, message);
|
|
||||||
|
|
||||||
(*progress_bars).insert(interpret_key(&interpret), progress_bar);
|
progress_tracker.add_task(§ion_key, &task_key, &message);
|
||||||
}
|
}
|
||||||
HarmonyEvent::InterpretExecutionFinished {
|
HarmonyEvent::InterpretExecutionFinished {
|
||||||
topology,
|
execution_id: task_key,
|
||||||
interpret,
|
topology: _,
|
||||||
score,
|
interpret: _,
|
||||||
|
score: _,
|
||||||
outcome,
|
outcome,
|
||||||
} => {
|
} => match outcome {
|
||||||
let has_topology = (*sections).contains_key(&topology_key(&topology));
|
Ok(outcome) => match outcome.status {
|
||||||
let section_key = if has_topology {
|
harmony::interpret::InterpretStatus::SUCCESS => {
|
||||||
topology_key(&topology)
|
progress_tracker.finish_task(&task_key, &outcome.message);
|
||||||
} else {
|
|
||||||
score_key(&score)
|
|
||||||
};
|
|
||||||
|
|
||||||
let section = (*sections).get(§ion_key).unwrap();
|
|
||||||
let progress_bar =
|
|
||||||
(*progress_bars).get(&interpret_key(&interpret)).cloned();
|
|
||||||
|
|
||||||
let _ = section.clear();
|
|
||||||
|
|
||||||
match outcome {
|
|
||||||
Ok(outcome) => match outcome.status {
|
|
||||||
harmony::interpret::InterpretStatus::SUCCESS => {
|
|
||||||
progress::success(section, progress_bar, outcome.message)
|
|
||||||
}
|
|
||||||
harmony::interpret::InterpretStatus::NOOP => {
|
|
||||||
progress::skip(section, progress_bar, outcome.message)
|
|
||||||
}
|
|
||||||
_ => progress::error(section, progress_bar, outcome.message),
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
progress::error(section, progress_bar, err.to_string());
|
|
||||||
}
|
}
|
||||||
|
harmony::interpret::InterpretStatus::NOOP => {
|
||||||
|
progress_tracker.skip_task(&task_key, &outcome.message);
|
||||||
|
}
|
||||||
|
_ => progress_tracker.fail_task(&task_key, &outcome.message),
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
progress_tracker.fail_task(&task_key, &err.to_string());
|
||||||
}
|
}
|
||||||
|
},
|
||||||
if !has_topology {
|
|
||||||
(*progress_bars).remove(§ion_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -181,7 +154,3 @@ fn topology_key(topology: &str) -> String {
|
|||||||
fn score_key(score: &str) -> String {
|
fn score_key(score: &str) -> String {
|
||||||
format!("score-{score}")
|
format!("score-{score}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interpret_key(interpret: &str) -> String {
|
|
||||||
format!("interpret-{interpret}")
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use clap::builder::ArgPredicate;
|
use clap::builder::ArgPredicate;
|
||||||
|
use harmony::instrumentation;
|
||||||
use harmony::inventory::Inventory;
|
use harmony::inventory::Inventory;
|
||||||
use harmony::maestro::Maestro;
|
use harmony::maestro::Maestro;
|
||||||
use harmony::{score::Score, topology::Topology};
|
use harmony::{score::Score, topology::Topology};
|
||||||
@ -97,6 +98,7 @@ pub async fn run<T: Topology + Send + Sync + 'static>(
|
|||||||
|
|
||||||
let result = init(maestro, args_struct).await;
|
let result = init(maestro, args_struct).await;
|
||||||
|
|
||||||
|
instrumentation::instrument(instrumentation::HarmonyEvent::HarmonyFinished).unwrap();
|
||||||
let _ = tokio::try_join!(cli_logger_handle);
|
let _ = tokio::try_join!(cli_logger_handle);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,163 @@
|
|||||||
|
use indicatif::{MultiProgress, ProgressBar};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use indicatif::{MultiProgress, ProgressBar};
|
pub trait ProgressTracker: Send + Sync {
|
||||||
|
fn contains_section(&self, id: &str) -> bool;
|
||||||
pub fn new_section(title: String) -> MultiProgress {
|
fn add_section(&self, id: &str, message: &str);
|
||||||
let multi_progress = MultiProgress::new();
|
fn add_task(&self, section_id: &str, task_id: &str, message: &str);
|
||||||
let _ = multi_progress.println(title);
|
fn finish_task(&self, id: &str, message: &str);
|
||||||
|
fn fail_task(&self, id: &str, message: &str);
|
||||||
multi_progress
|
fn skip_task(&self, id: &str, message: &str);
|
||||||
|
fn clear(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_spinner(multi_progress: &MultiProgress, message: String) -> ProgressBar {
|
struct Section {
|
||||||
let progress = multi_progress.add(ProgressBar::new_spinner());
|
header_index: usize,
|
||||||
|
task_count: usize,
|
||||||
progress.set_style(crate::theme::SPINNER_STYLE.clone());
|
pb: ProgressBar,
|
||||||
progress.set_message(message);
|
|
||||||
progress.enable_steady_tick(Duration::from_millis(100));
|
|
||||||
|
|
||||||
progress
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn success(multi_progress: &MultiProgress, progress: Option<ProgressBar>, message: String) {
|
struct IndicatifProgressTrackerState {
|
||||||
if let Some(progress) = progress {
|
sections: HashMap<String, Section>,
|
||||||
multi_progress.remove(&progress)
|
tasks: HashMap<String, ProgressBar>,
|
||||||
|
pb_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct IndicatifProgressTracker {
|
||||||
|
mp: MultiProgress,
|
||||||
|
state: Arc<Mutex<IndicatifProgressTrackerState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndicatifProgressTracker {
|
||||||
|
pub fn new(base: MultiProgress) -> Self {
|
||||||
|
// The indicatif log bridge will insert a progress bar at the top.
|
||||||
|
// To prevent our first section from being erased, we need to create
|
||||||
|
// a dummy progress bar as our first progress bar.
|
||||||
|
let _ = base.clear();
|
||||||
|
let log_pb = base.add(ProgressBar::new(1));
|
||||||
|
|
||||||
|
let mut sections = HashMap::new();
|
||||||
|
sections.insert(
|
||||||
|
"__log__".into(),
|
||||||
|
Section {
|
||||||
|
header_index: 0,
|
||||||
|
task_count: 0,
|
||||||
|
pb: log_pb.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut tasks = HashMap::new();
|
||||||
|
tasks.insert("__log__".into(), log_pb);
|
||||||
|
|
||||||
|
let state = Arc::new(Mutex::new(IndicatifProgressTrackerState {
|
||||||
|
sections,
|
||||||
|
tasks,
|
||||||
|
pb_count: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
Self { mp: base, state }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgressTracker for IndicatifProgressTracker {
|
||||||
|
fn add_section(&self, id: &str, message: &str) {
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
|
let header_pb = self
|
||||||
|
.mp
|
||||||
|
.add(ProgressBar::new(1).with_style(crate::theme::SECTION_STYLE.clone()));
|
||||||
|
header_pb.finish_with_message(message.to_string());
|
||||||
|
|
||||||
|
let header_index = state.pb_count;
|
||||||
|
state.pb_count += 1;
|
||||||
|
|
||||||
|
state.sections.insert(
|
||||||
|
id.to_string(),
|
||||||
|
Section {
|
||||||
|
header_index,
|
||||||
|
task_count: 0,
|
||||||
|
pb: header_pb,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = multi_progress.add(ProgressBar::new_spinner());
|
fn add_task(&self, section_id: &str, task_id: &str, message: &str) {
|
||||||
progress.set_style(crate::theme::SUCCESS_SPINNER_STYLE.clone());
|
let mut state = self.state.lock().unwrap();
|
||||||
progress.finish_with_message(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn error(multi_progress: &MultiProgress, progress: Option<ProgressBar>, message: String) {
|
let insertion_index = {
|
||||||
if let Some(progress) = progress {
|
let current_section = state
|
||||||
multi_progress.remove(&progress)
|
.sections
|
||||||
|
.get(section_id)
|
||||||
|
.expect("Section ID not found");
|
||||||
|
current_section.header_index + current_section.task_count + 1 // +1 to insert after header
|
||||||
|
};
|
||||||
|
|
||||||
|
let pb = self.mp.insert(insertion_index, ProgressBar::new_spinner());
|
||||||
|
pb.set_style(crate::theme::SPINNER_STYLE.clone());
|
||||||
|
pb.set_prefix(" ");
|
||||||
|
pb.set_message(message.to_string());
|
||||||
|
pb.enable_steady_tick(Duration::from_millis(80));
|
||||||
|
|
||||||
|
state.pb_count += 1;
|
||||||
|
|
||||||
|
let section = state
|
||||||
|
.sections
|
||||||
|
.get_mut(section_id)
|
||||||
|
.expect("Section ID not found");
|
||||||
|
section.task_count += 1;
|
||||||
|
|
||||||
|
// We inserted a new progress bar, so we must update the header_index
|
||||||
|
// for all subsequent sections.
|
||||||
|
for (id, s) in state.sections.iter_mut() {
|
||||||
|
if id != section_id && s.header_index >= insertion_index {
|
||||||
|
s.header_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.tasks.insert(task_id.to_string(), pb);
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = multi_progress.add(ProgressBar::new_spinner());
|
fn finish_task(&self, id: &str, message: &str) {
|
||||||
progress.set_style(crate::theme::ERROR_SPINNER_STYLE.clone());
|
let state = self.state.lock().unwrap();
|
||||||
progress.finish_with_message(message);
|
if let Some(pb) = state.tasks.get(id) {
|
||||||
}
|
pb.set_style(crate::theme::SUCCESS_SPINNER_STYLE.clone());
|
||||||
|
pb.finish_with_message(message.to_string());
|
||||||
pub fn skip(multi_progress: &MultiProgress, progress: Option<ProgressBar>, message: String) {
|
}
|
||||||
if let Some(progress) = progress {
|
|
||||||
multi_progress.remove(&progress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = multi_progress.add(ProgressBar::new_spinner());
|
fn fail_task(&self, id: &str, message: &str) {
|
||||||
progress.set_style(crate::theme::SKIP_SPINNER_STYLE.clone());
|
let state = self.state.lock().unwrap();
|
||||||
progress.finish_with_message(message);
|
if let Some(pb) = state.tasks.get(id) {
|
||||||
|
pb.set_style(crate::theme::ERROR_SPINNER_STYLE.clone());
|
||||||
|
pb.finish_with_message(message.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_task(&self, id: &str, message: &str) {
|
||||||
|
let state = self.state.lock().unwrap();
|
||||||
|
if let Some(pb) = state.tasks.get(id) {
|
||||||
|
pb.set_style(crate::theme::SKIP_SPINNER_STYLE.clone());
|
||||||
|
pb.finish_with_message(message.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_section(&self, id: &str) -> bool {
|
||||||
|
let state = self.state.lock().unwrap();
|
||||||
|
state.sections.contains_key(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&self) {
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
|
state.tasks.values().for_each(|p| self.mp.remove(p));
|
||||||
|
state.tasks.clear();
|
||||||
|
state.sections.values().for_each(|s| self.mp.remove(&s.pb));
|
||||||
|
state.sections.clear();
|
||||||
|
state.pb_count = 0;
|
||||||
|
|
||||||
|
let _ = self.mp.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,11 @@ pub static EMOJI_TOPOLOGY: Emoji<'_, '_> = Emoji("📦", "");
|
|||||||
pub static EMOJI_SCORE: Emoji<'_, '_> = Emoji("🎶", "");
|
pub static EMOJI_SCORE: Emoji<'_, '_> = Emoji("🎶", "");
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
pub static ref SECTION_STYLE: ProgressStyle = ProgressStyle::default_spinner()
|
||||||
|
.template("{wide_msg:.bold}")
|
||||||
|
.unwrap();
|
||||||
pub static ref SPINNER_STYLE: ProgressStyle = ProgressStyle::default_spinner()
|
pub static ref SPINNER_STYLE: ProgressStyle = ProgressStyle::default_spinner()
|
||||||
.template(" {spinner:.green} {msg}")
|
.template(" {spinner:.green} {wide_msg}")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]);
|
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]);
|
||||||
pub static ref SUCCESS_SPINNER_STYLE: ProgressStyle = SPINNER_STYLE
|
pub static ref SUCCESS_SPINNER_STYLE: ProgressStyle = SPINNER_STYLE
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use indicatif::{MultiProgress, ProgressBar};
|
use harmony_cli::progress::{IndicatifProgressTracker, ProgressTracker};
|
||||||
use indicatif_log_bridge::LogWrapper;
|
use indicatif::MultiProgress;
|
||||||
use log::error;
|
use log::error;
|
||||||
use std::{
|
use std::sync::Arc;
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::instrumentation::{self, HarmonyComposerEvent};
|
use crate::instrumentation::{self, HarmonyComposerEvent};
|
||||||
|
|
||||||
@ -22,85 +19,57 @@ pub fn init() -> tokio::task::JoinHandle<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn configure_logger() {
|
fn configure_logger() {
|
||||||
let logger =
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).build();
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).build();
|
|
||||||
let level = logger.filter();
|
|
||||||
let multi = MultiProgress::new();
|
|
||||||
LogWrapper::new(multi.clone(), logger).try_init().unwrap();
|
|
||||||
log::set_max_level(level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_events() {
|
pub async fn handle_events() {
|
||||||
const PROGRESS_SETUP: &str = "project-initialization";
|
let progress_tracker = Arc::new(IndicatifProgressTracker::new(MultiProgress::new()));
|
||||||
|
|
||||||
|
const SETUP_SECTION: &str = "project-initialization";
|
||||||
|
const COMPILTATION_TASK: &str = "compilation";
|
||||||
const PROGRESS_DEPLOYMENT: &str = "deployment";
|
const PROGRESS_DEPLOYMENT: &str = "deployment";
|
||||||
|
|
||||||
instrumentation::subscribe("Harmony Composer Logger", {
|
instrumentation::subscribe("Harmony Composer Logger", {
|
||||||
let progresses: Arc<Mutex<HashMap<String, MultiProgress>>> =
|
|
||||||
Arc::new(Mutex::new(HashMap::new()));
|
|
||||||
let compilation_progress = Arc::new(Mutex::new(None::<ProgressBar>));
|
|
||||||
|
|
||||||
move |event| {
|
move |event| {
|
||||||
let progresses_clone = Arc::clone(&progresses);
|
let progress_tracker = Arc::clone(&progress_tracker);
|
||||||
let compilation_progress_clone = Arc::clone(&compilation_progress);
|
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let mut progresses_guard = progresses_clone.lock().unwrap();
|
|
||||||
let mut compilation_progress_guard = compilation_progress_clone.lock().unwrap();
|
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
HarmonyComposerEvent::HarmonyComposerStarted => {}
|
HarmonyComposerEvent::HarmonyComposerStarted => {}
|
||||||
HarmonyComposerEvent::ProjectInitializationStarted => {
|
HarmonyComposerEvent::ProjectInitializationStarted => {
|
||||||
let multi_progress = harmony_cli::progress::new_section(format!(
|
progress_tracker.add_section(
|
||||||
"{} Initializing Harmony project...",
|
SETUP_SECTION,
|
||||||
harmony_cli::theme::EMOJI_HARMONY,
|
&format!(
|
||||||
));
|
"{} Initializing Harmony project...",
|
||||||
(*progresses_guard).insert(PROGRESS_SETUP.to_string(), multi_progress);
|
harmony_cli::theme::EMOJI_HARMONY,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
HarmonyComposerEvent::ProjectInitialized => println!("\n"),
|
HarmonyComposerEvent::ProjectInitialized => {}
|
||||||
HarmonyComposerEvent::ProjectCompilationStarted { details } => {
|
HarmonyComposerEvent::ProjectCompilationStarted { details } => {
|
||||||
let initialization_progress =
|
progress_tracker.add_task(SETUP_SECTION, COMPILTATION_TASK, &details);
|
||||||
(*progresses_guard).get(PROGRESS_SETUP).unwrap();
|
|
||||||
let _ = initialization_progress.clear();
|
|
||||||
|
|
||||||
let progress =
|
|
||||||
harmony_cli::progress::add_spinner(initialization_progress, details);
|
|
||||||
*compilation_progress_guard = Some(progress);
|
|
||||||
}
|
}
|
||||||
HarmonyComposerEvent::ProjectCompiled => {
|
HarmonyComposerEvent::ProjectCompiled => {
|
||||||
let initialization_progress =
|
progress_tracker.finish_task(COMPILTATION_TASK, "project compiled");
|
||||||
(*progresses_guard).get(PROGRESS_SETUP).unwrap();
|
|
||||||
|
|
||||||
harmony_cli::progress::success(
|
|
||||||
initialization_progress,
|
|
||||||
(*compilation_progress_guard).take(),
|
|
||||||
"project compiled".to_string(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
HarmonyComposerEvent::ProjectCompilationFailed { details } => {
|
HarmonyComposerEvent::ProjectCompilationFailed { details } => {
|
||||||
let initialization_progress =
|
progress_tracker.fail_task(COMPILTATION_TASK, "failed to compile project");
|
||||||
(*progresses_guard).get(PROGRESS_SETUP).unwrap();
|
|
||||||
|
|
||||||
harmony_cli::progress::error(
|
|
||||||
initialization_progress,
|
|
||||||
(*compilation_progress_guard).take(),
|
|
||||||
"failed to compile project".to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
error!("{details}");
|
error!("{details}");
|
||||||
}
|
}
|
||||||
HarmonyComposerEvent::DeploymentStarted { target } => {
|
HarmonyComposerEvent::DeploymentStarted { target } => {
|
||||||
let multi_progress = harmony_cli::progress::new_section(format!(
|
progress_tracker.add_section(
|
||||||
"{} Starting deployment to {target}...\n\n",
|
PROGRESS_DEPLOYMENT,
|
||||||
harmony_cli::theme::EMOJI_DEPLOY
|
&format!(
|
||||||
));
|
"\n{} Deploying project to {target}...\n",
|
||||||
(*progresses_guard).insert(PROGRESS_DEPLOYMENT.to_string(), multi_progress);
|
harmony_cli::theme::EMOJI_DEPLOY,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
HarmonyComposerEvent::DeploymentCompleted => {
|
||||||
|
progress_tracker.clear();
|
||||||
}
|
}
|
||||||
HarmonyComposerEvent::DeploymentCompleted => println!("\n"),
|
|
||||||
HarmonyComposerEvent::Shutdown => {
|
HarmonyComposerEvent::Shutdown => {
|
||||||
for (_, progresses) in (*progresses_guard).iter() {
|
|
||||||
progresses.clear().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user