use ic_query::{
QueryProgress, QueryProgressEvent, QueryProgressState, subnet_catalog::MAINNET_NETWORK,
};
use std::{
io::{self, IsTerminal, Write},
path::Path,
};
#[derive(Debug)]
pub struct StderrQueryProgress {
line: ProgressLine,
}
impl StderrQueryProgress {
pub fn new() -> Self {
Self {
line: ProgressLine::stderr(),
}
}
}
impl QueryProgress for StderrQueryProgress {
fn report(&mut self, event: QueryProgressEvent) {
match event {
QueryProgressEvent::CacheRefresh {
component,
path,
source_endpoint,
} => {
self.line.clear();
eprintln!(
"{component} cache missing at {}; calling {source_endpoint} to refresh/create cache",
path.display()
);
}
QueryProgressEvent::PagedRefresh { text, state } => {
if state == QueryProgressState::Running {
self.line.update(&text);
} else {
self.line.finish(&text);
}
}
}
}
}
pub fn announce_missing_mainnet_cache(
network: &str,
component: &str,
path: &Path,
source_endpoint: &str,
) {
if network != MAINNET_NETWORK || path.is_file() {
return;
}
let mut progress = StderrQueryProgress::new();
progress.report(QueryProgressEvent::CacheRefresh {
component: component.to_string(),
path: path.to_path_buf(),
source_endpoint: source_endpoint.to_string(),
});
}
#[derive(Debug)]
struct ProgressLine {
enabled: bool,
last_width: usize,
stderr: io::Stderr,
}
impl ProgressLine {
fn stderr() -> Self {
Self {
enabled: io::stderr().is_terminal(),
last_width: 0,
stderr: io::stderr(),
}
}
fn update(&mut self, text: &str) {
if !self.enabled {
return;
}
self.write_line(text, false);
}
fn finish(&mut self, text: &str) {
if !self.enabled {
return;
}
self.write_line(text, true);
self.last_width = 0;
}
fn clear(&mut self) {
if !self.enabled || self.last_width == 0 {
return;
}
let padding = " ".repeat(self.last_width);
let _ = write!(self.stderr, "\r{padding}\r");
let _ = self.stderr.flush();
self.last_width = 0;
}
fn write_line(&mut self, text: &str, newline: bool) {
let width = text.chars().count();
let padding = " ".repeat(self.last_width.saturating_sub(width));
if newline {
let _ = writeln!(self.stderr, "\r{text}{padding}");
} else {
let _ = write!(self.stderr, "\r{text}{padding}");
}
let _ = self.stderr.flush();
self.last_width = width;
}
}
impl Drop for ProgressLine {
fn drop(&mut self) {
self.clear();
}
}