thedes_async_util/
progress.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicUsize, Ordering::*},
4};
5
6pub fn open(goal: usize) -> (Logger, Monitor) {
7    let shared = Arc::new(Shared {
8        goal,
9        current: AtomicUsize::new(0),
10        status: std::sync::Mutex::new(Vec::new()),
11    });
12
13    let logger =
14        Logger { shared: shared.clone(), status_index: shared.enter() };
15    let monitor = Monitor { shared };
16
17    (logger, monitor)
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct Progress {
22    current: usize,
23    status: String,
24}
25
26impl Progress {
27    pub fn current(&self) -> usize {
28        self.current
29    }
30
31    pub fn status(&self) -> &str {
32        &self.status
33    }
34}
35
36#[derive(Debug)]
37struct Shared {
38    goal: usize,
39    current: AtomicUsize,
40    status: std::sync::Mutex<Vec<Option<Box<str>>>>,
41}
42
43impl Shared {
44    pub fn goal(&self) -> usize {
45        self.goal
46    }
47
48    pub fn read_status(&self) -> String {
49        let statuses = self.status.lock().expect("poisoned lock");
50        let mut status = String::new();
51
52        for maybe in &*statuses {
53            if let Some(item) = maybe {
54                if !status.is_empty() {
55                    status += " > ";
56                }
57                status += item;
58            }
59        }
60
61        status
62    }
63
64    pub fn read_current(&self) -> usize {
65        self.current.load(Relaxed)
66    }
67
68    pub fn read(&self) -> Progress {
69        Progress { status: self.read_status(), current: self.read_current() }
70    }
71
72    pub fn increment(&self) {
73        self.current.fetch_add(1, Relaxed);
74    }
75
76    pub fn set_status(&self, index: usize, status: &str) {
77        self.status.lock().expect("poisoned lock")[index] = Some(status.into());
78    }
79
80    pub fn enter(&self) -> usize {
81        let mut statuses = self.status.lock().expect("poisoned lock");
82        let index = statuses.len();
83        statuses.push(Some(Box::default()));
84        index
85    }
86
87    pub fn leave(&self, index: usize) {
88        let mut statuses = self.status.lock().expect("poisoned lock");
89        statuses[index] = None;
90
91        while statuses.last().is_some_and(Option::is_none) {
92            statuses.pop();
93        }
94    }
95}
96
97#[derive(Debug, Clone)]
98pub struct Monitor {
99    shared: Arc<Shared>,
100}
101
102impl Monitor {
103    pub fn goal(&self) -> usize {
104        self.shared.goal()
105    }
106
107    pub fn read(&self) -> Progress {
108        self.shared.read()
109    }
110}
111
112#[derive(Debug)]
113pub struct Logger {
114    shared: Arc<Shared>,
115    status_index: usize,
116}
117
118impl Logger {
119    pub fn goal(&self) -> usize {
120        self.shared.goal()
121    }
122
123    pub fn read(&self) -> Progress {
124        self.shared.read()
125    }
126
127    pub fn increment(&self) {
128        self.shared.increment();
129    }
130
131    pub fn set_status(&self, status: &str) {
132        self.shared.set_status(self.status_index, status);
133    }
134
135    pub fn nest(&self) -> Logger {
136        Self { shared: self.shared.clone(), status_index: self.shared.enter() }
137    }
138}
139
140impl Drop for Logger {
141    fn drop(&mut self) {
142        self.shared.leave(self.status_index);
143    }
144}