use rusty_bubbletea::commands;
use rusty_bubbletea::model::{Cmd, Msg};
use rusty_lipgloss::Style;
use std::sync::atomic::{AtomicI64, Ordering};
use std::time::{Duration, SystemTime};
static LAST_ID: AtomicI64 = AtomicI64::new(0);
fn next_id() -> i32 {
(LAST_ID.fetch_add(1, Ordering::SeqCst)) as i32
}
#[derive(Debug, Clone)]
pub struct Spinner {
pub frames: Vec<String>,
pub fps: Duration,
}
pub fn line() -> Spinner {
Spinner {
frames: vec![
"|".to_string(),
"/".to_string(),
"-".to_string(),
"\\".to_string(),
],
fps: Duration::from_millis(100),
}
}
pub fn dot() -> Spinner {
Spinner {
frames: vec![
"⣾ ".to_string(),
"⣽ ".to_string(),
"⣻ ".to_string(),
"⢿ ".to_string(),
"⡿ ".to_string(),
"⣟ ".to_string(),
"⣯ ".to_string(),
"⣷ ".to_string(),
],
fps: Duration::from_millis(100),
}
}
pub fn mini_dot() -> Spinner {
Spinner {
frames: vec![
"⠋".to_string(),
"⠙".to_string(),
"⠹".to_string(),
"⠸".to_string(),
"⠼".to_string(),
"⠴".to_string(),
"⠦".to_string(),
"⠧".to_string(),
"⠇".to_string(),
"⠏".to_string(),
],
fps: Duration::from_millis(83),
}
}
pub fn jump() -> Spinner {
Spinner {
frames: vec![
"⢄".to_string(),
"⢂".to_string(),
"⢁".to_string(),
"⡁".to_string(),
"⡈".to_string(),
"⡐".to_string(),
"⡠".to_string(),
],
fps: Duration::from_millis(100),
}
}
pub fn pulse() -> Spinner {
Spinner {
frames: vec![
"█".to_string(),
"▓".to_string(),
"▒".to_string(),
"░".to_string(),
],
fps: Duration::from_millis(125),
}
}
pub fn points() -> Spinner {
Spinner {
frames: vec![
"∙∙∙".to_string(),
"●∙∙".to_string(),
"∙●∙".to_string(),
"∙∙●".to_string(),
],
fps: Duration::from_millis(142),
}
}
pub fn globe() -> Spinner {
Spinner {
frames: vec!["🌍".to_string(), "🌎".to_string(), "🌏".to_string()],
fps: Duration::from_millis(250),
}
}
pub fn moon() -> Spinner {
Spinner {
frames: vec![
"🌑".to_string(),
"🌒".to_string(),
"🌓".to_string(),
"🌔".to_string(),
"🌕".to_string(),
"🌖".to_string(),
"🌗".to_string(),
"🌘".to_string(),
],
fps: Duration::from_millis(125),
}
}
pub fn monkey() -> Spinner {
Spinner {
frames: vec!["🙈".to_string(), "🙉".to_string(), "🙊".to_string()],
fps: Duration::from_millis(333),
}
}
pub fn meter() -> Spinner {
Spinner {
frames: vec![
"▱▱▱".to_string(),
"▰▱▱".to_string(),
"▰▰▱".to_string(),
"▰▰▰".to_string(),
"▰▰▱".to_string(),
"▰▱▱".to_string(),
"▱▱▱".to_string(),
],
fps: Duration::from_millis(142),
}
}
pub fn hamburger() -> Spinner {
Spinner {
frames: vec![
"☱".to_string(),
"☲".to_string(),
"☴".to_string(),
"☲".to_string(),
],
fps: Duration::from_millis(333),
}
}
pub fn ellipsis() -> Spinner {
Spinner {
frames: vec![
"".to_string(),
".".to_string(),
"..".to_string(),
"...".to_string(),
],
fps: Duration::from_millis(333),
}
}
pub struct Model {
pub spinner: Spinner,
pub style: Style,
frame: usize,
id: i32,
tag: i32,
}
impl std::fmt::Debug for Model {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("spinner::Model")
.field("id", &self.id)
.field("frame", &self.frame)
.finish()
}
}
impl Model {
pub fn id(&self) -> i32 {
self.id
}
pub fn update(&mut self, msg: &dyn Msg) -> Cmd {
if let Some(m) = msg.as_any().downcast_ref::<TickMsg>() {
if m.id > 0 && m.id != self.id {
return None;
}
if m.tag > 0 && m.tag != self.tag {
return None;
}
self.frame += 1;
if self.frame >= self.spinner.frames.len() {
self.frame = 0;
}
self.tag += 1;
return self.tick(self.id, self.tag);
}
None
}
pub fn view(&self) -> String {
if self.frame >= self.spinner.frames.len() {
return "(error)".to_string();
}
self.style.render(&self.spinner.frames[self.frame])
}
pub fn tick_msg(&self) -> TickMsg {
TickMsg {
time: SystemTime::now(),
id: self.id,
tag: self.tag,
}
}
fn tick(&self, id: i32, tag: i32) -> Cmd {
let fps = self.spinner.fps;
commands::tick(fps, move |t| Some(Box::new(TickMsg { time: t, id, tag })))
}
}
#[derive(Debug, Clone)]
pub struct TickMsg {
pub time: SystemTime,
tag: i32,
pub id: i32,
}
pub type Option = Box<dyn FnOnce(&mut Model)>;
pub fn with_spinner(spinner: Spinner) -> Option {
Box::new(move |m: &mut Model| {
m.spinner = spinner;
})
}
pub fn with_style(style: Style) -> Option {
Box::new(move |m: &mut Model| {
m.style = style;
})
}
pub fn new(opts: Vec<Option>) -> Model {
let mut m = Model {
spinner: line(),
id: next_id(),
frame: 0,
tag: 0,
style: Style::new(),
};
for opt in opts {
opt(&mut m);
}
m
}