use super::EffectActor;
use crate::runtime::{Molecule, ReactorHandle};
use crate::value::Value;
use crate::Error;
use async_trait::async_trait;
use indicatif::{ProgressBar, ProgressStyle};
use parking_lot::Mutex;
use std::sync::Arc;
use std::time::Duration;
pub struct SpinnerActor {
state: Arc<Mutex<Option<ProgressBar>>>,
}
impl SpinnerActor {
pub fn new() -> Self {
Self {
state: Arc::new(Mutex::new(None)),
}
}
}
impl Default for SpinnerActor {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl EffectActor for SpinnerActor {
fn kind_name(&self) -> &'static str {
"Spinner"
}
async fn run(&self, request: Molecule, handle: ReactorHandle) -> Result<(), Error> {
let running = request
.fields
.get("running")
.and_then(|v| if let Value::Bool(b) = v { Some(*b) } else { None })
.unwrap_or(false);
let label = request
.fields
.get("label")
.and_then(|v| if let Value::String(s) = v { Some(s.clone()) } else { None })
.unwrap_or_default();
{
let mut state = self.state.lock();
if running {
if let Some(pb) = state.as_ref() {
pb.set_message(label);
} else {
let pb = ProgressBar::new_spinner();
let style = ProgressStyle::with_template("{spinner:.cyan} {msg}")
.unwrap_or_else(|_| ProgressStyle::default_spinner());
pb.set_style(style.tick_strings(&[
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏",
]));
pb.set_message(label);
pb.enable_steady_tick(Duration::from_millis(80));
*state = Some(pb);
}
} else if let Some(pb) = state.take() {
pb.finish_and_clear();
}
}
let mut updated = request.clone();
updated
.fields
.insert("status".into(), Value::String("Done".into()));
handle.emit(updated).await
}
}