broot/git/status_computer.rs
1use {
2 super::TreeGitStatus,
3 crate::{
4 git,
5 task_sync::{
6 Computation,
7 ComputationResult,
8 Dam,
9 },
10 },
11 git2::Repository,
12 once_cell::sync::Lazy,
13 rustc_hash::FxHashMap,
14 std::{
15 path::{
16 Path,
17 PathBuf,
18 },
19 sync::Mutex,
20 },
21 termimad::crossbeam::channel::bounded,
22};
23
24fn compute_tree_status(root_path: &Path) -> ComputationResult<TreeGitStatus> {
25 match Repository::open(root_path) {
26 Ok(git_repo) => {
27 let tree_git_status = time!(TreeGitStatus::from(&git_repo),);
28 match tree_git_status {
29 Some(gs) => ComputationResult::Done(gs),
30 None => ComputationResult::None,
31 }
32 }
33 Err(e) => {
34 debug!("failed to discover repo: {:?}", e);
35 ComputationResult::None
36 }
37 }
38}
39
40// the key is the path of the repository
41static TS_CACHE_MX: Lazy<Mutex<FxHashMap<PathBuf, Computation<TreeGitStatus>>>> =
42 Lazy::new(|| Mutex::new(FxHashMap::default()));
43
44/// try to get the result of the computation of the tree git status.
45/// This may be immediate if a previous computation was finished.
46/// This may wait for the result of a new computation or of a previously
47/// launched one.
48/// In any case:
49/// - this function returns as soon as the dam asks for it (ie when there's an event)
50/// - computations are never dropped unless the program ends: they continue in background
51/// and the result may be available for following queries
52pub fn get_tree_status(
53 root_path: &Path,
54 dam: &mut Dam,
55) -> ComputationResult<TreeGitStatus> {
56 match git::closest_repo_dir(root_path) {
57 None => ComputationResult::None,
58 Some(repo_path) => {
59 let comp = TS_CACHE_MX
60 .lock()
61 .unwrap()
62 .get(&repo_path)
63 .map(|c| (*c).clone());
64 match comp {
65 Some(Computation::Finished(comp_res)) => {
66 // already computed
67 comp_res
68 }
69 Some(Computation::InProgress(comp_receiver)) => {
70 // computation in progress
71 // We do a select! to wait for either the dam
72 // or the receiver
73 debug!("start select on in progress computation");
74 dam.select(comp_receiver)
75 }
76 None => {
77 // not yet started. We launch the computation and store
78 // the receiver immediately.
79 // We use the dam to return from this function when
80 // needed (while letting the underlying thread finish
81 // the job)
82 //
83 // note: must also update the TS_CACHE entry at end
84 let (s, r) = bounded(1);
85 TS_CACHE_MX
86 .lock()
87 .unwrap()
88 .insert(repo_path.clone(), Computation::InProgress(r));
89 dam.try_compute(move || {
90 let comp_res = compute_tree_status(&repo_path);
91 TS_CACHE_MX
92 .lock()
93 .unwrap()
94 .insert(repo_path.clone(), Computation::Finished(comp_res.clone()));
95 if let Err(e) = s.send(comp_res.clone()) {
96 debug!("error while sending comp result: {:?}", e);
97 }
98 comp_res
99 })
100 }
101 }
102 }
103 }
104}
105
106/// clear the finished or in progress computation.
107/// Limit: we may receive in cache the result of a computation
108/// which started before the clear (if this is a problem we could
109/// store a cleaning counter alongside the cache to prevent insertions)
110pub fn clear_status_computer_cache() {
111 let mut ts_cache = TS_CACHE_MX.lock().unwrap();
112 ts_cache.clear();
113}