rfs_runner/templates/
mod.rs

1use std::collections::HashMap;
2use std::fmt::{Debug, Display, Formatter, Write};
3use std::sync::atomic::{AtomicUsize, Ordering};
4use std::sync::{Arc, RwLock};
5
6use colored::Colorize;
7use indicatif::{HumanCount, HumanDuration, ProgressState, ProgressStyle};
8
9use crate::Uid;
10
11pub trait WorkerTemplate: Send + Sync + Clone + AsProgressStyle + 'static {
12  fn new(label: impl ToString) -> Self;
13  fn increment(&self, count: usize);
14  fn add_worker(&self, id: Uid, body: String);
15  fn remove_worker(&self, id: &Uid);
16  fn edit_worker(&self, id: &Uid, body: String);
17}
18
19pub trait AsProgressStyle {
20  fn as_progress_style(&self) -> ProgressStyle;
21}
22
23#[derive(Debug)]
24pub struct DefaultTemplate
25where
26  Self: Send + Sync,
27{
28  tbody: Arc<RwLock<HashMap<Uid, String>>>,
29  footer: Arc<str>,
30  main_key: Arc<str>,
31  main_count: Arc<AtomicUsize>,
32}
33
34impl DefaultTemplate {
35  const TABLE_WIDTH: usize = 120;
36
37  fn main_count(&self) -> usize {
38    self.main_count.load(Ordering::SeqCst)
39  }
40
41  fn header(&self) -> String {
42    let main_count = self.main_count().to_string();
43    let pad_left = Self::TABLE_WIDTH - self.main_key.len() - (main_count.len() + 2) - 20 - 20 - 4;
44    format!(
45      "╭{main_key}: {main_count} {top_fill}{{pos_pad_left}}/{{len_pad_right}}╮\n",
46      main_key = self.main_key.bright_blue(),
47      main_count = main_count.bright_green(),
48      top_fill = "─".repeat(pad_left)
49    )
50  }
51
52  fn body(&self) -> String {
53    self
54      .tbody
55      .read()
56      .unwrap()
57      .clone()
58      .into_values()
59      .map(|value| format!("│ {value:>114} │\n"))
60      .collect()
61  }
62}
63
64impl WorkerTemplate for DefaultTemplate {
65  fn new(label: impl ToString) -> Self {
66    let footer = "\
67    │ {prefix:!116} │\n\
68    │ {msg:!116} │\n\
69    │ Elapsed: {elapsed_precise:.blue} │ Est. Time remaining: {human_eta:!30} │ P/Sec: {per_sec:!35} │\n\
70    ╰{bar:!118.green/.cyan.dim}╯\n\
71    ";
72    Self {
73      tbody: Default::default(),
74      footer: Arc::from(footer),
75      main_key: Arc::from(label.to_string()),
76      main_count: Default::default(),
77    }
78  }
79
80  fn increment(&self, count: usize) {
81    self.main_count.fetch_add(count, Ordering::AcqRel);
82  }
83
84  fn add_worker(&self, id: Uid, body: String) {
85    self.tbody.write().unwrap().insert(id, body);
86  }
87
88  fn remove_worker(&self, id: &Uid) {
89    if self.tbody.read().unwrap().contains_key(id) {
90      self.tbody.write().unwrap().remove(id);
91    }
92  }
93
94  fn edit_worker(&self, id: &Uid, body: String) {
95    if !self.tbody.read().unwrap().contains_key(id) {
96      return;
97    }
98
99    if let Some(inner) = self.tbody.write().unwrap().get_mut(id) {
100      *inner = body
101    }
102  }
103}
104
105impl AsProgressStyle for DefaultTemplate {
106  fn as_progress_style(&self) -> ProgressStyle {
107    self.into()
108  }
109}
110
111impl Display for DefaultTemplate {
112  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
113    write!(f, "{}{}{}", self.header(), self.body(), self.footer)
114  }
115}
116
117impl Clone for DefaultTemplate {
118  fn clone(&self) -> Self {
119    Self {
120      tbody: Arc::clone(&self.tbody),
121      footer: Arc::clone(&self.footer),
122      main_key: Arc::clone(&self.main_key),
123      main_count: Arc::clone(&self.main_count),
124    }
125  }
126}
127
128impl From<&DefaultTemplate> for ProgressStyle {
129  fn from(value: &DefaultTemplate) -> ProgressStyle {
130    ProgressStyle::with_template(value.to_string().as_str())
131      .unwrap()
132      .progress_chars("──")
133      .tick_strings(&["◜", "◠", "◝", "◞", "◡", "◟"])
134      .with_key("human_eta", |s: &ProgressState, w: &mut dyn Write| match s.eta().as_secs() {
135        0..=600 => write!(w, "{}", HumanDuration(s.eta()).to_string().bright_green()).unwrap(),
136        601..=6000 => write!(w, "{}", HumanDuration(s.eta()).to_string().bright_yellow()).unwrap(),
137        _ => write!(w, "{}", HumanDuration(s.eta()).to_string().bright_red()).unwrap(),
138      })
139      .with_key("len_pad_left", |s: &ProgressState, w: &mut dyn Write| {
140        let Some(len) = s.len().map(HumanCount) else { return };
141        let len = len.to_string();
142        let pad = "─".repeat(19 - len.len());
143        write!(w, "{pad} {}", len.bright_cyan()).unwrap();
144      })
145      .with_key("len_pad_right", |s: &ProgressState, w: &mut dyn Write| {
146        let Some(len) = s.len().map(HumanCount) else { return };
147        let len = len.to_string();
148        let pad = "─".repeat(19 - len.len());
149        write!(w, "{} {pad}", len.bright_cyan()).unwrap();
150      })
151      .with_key("pos_pad_left", |s: &ProgressState, w: &mut dyn Write| {
152        let len = HumanCount(s.pos()).to_string();
153        let pad = "─".repeat(19 - len.len());
154        write!(w, "{pad} {}", len.bright_green().bold()).unwrap();
155      })
156      .with_key("pos_pad_right", |s: &ProgressState, w: &mut dyn Write| {
157        let len = HumanCount(s.pos()).to_string();
158        let pad = "─".repeat(19 - len.len());
159        write!(w, "{} {pad}", len.bright_green().bold()).unwrap();
160      })
161  }
162}