rfs_runner/templates/
mod.rs1use 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}