1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
use crate::{
http::{self, Ext},
nix,
};
use anyhow::Result;
use futures::{stream, StreamExt, TryStreamExt};
use indicatif::{ProgressBar, ProgressStyle};
use tracing::instrument;
pub trait Run {
async fn run(&self) -> Result<()>;
}
impl Run for crate::Cli {
#[instrument(skip(self))]
async fn run(&self) -> Result<()> {
let store_paths = if let Some(installables) = self.installables.clone() {
resolve_installables(installables).await?
} else if let Some(configuration) = &self.configuration {
println!("ā Indexing requisites of configuration closure");
nix::configuration_closure_paths(configuration)?
} else {
println!("ā Indexing all installables of flake `{}`", self.flake);
let installables = nix::all_flake_installables(&self.flake)?;
resolve_installables(installables).await?
};
check_store_paths(&self.binary_caches, &store_paths, self.show_missing).await?;
Ok(())
}
}
#[instrument(skip(installables))]
async fn resolve_installables(installables: Vec<String>) -> Result<Vec<String>> {
println!(
"š Attempting to evaluate {} installable(s)",
installables.len()
);
// Find our outputs concurrently
let progress_bar = ProgressBar::new(installables.len() as u64).with_style(progress_style()?);
let out_paths: Vec<String> = stream::iter(&installables)
.map(|installable| {
let progress_bar = &progress_bar;
async move {
progress_bar.inc(1);
let out_path = nix::out_path(installable)?;
anyhow::Ok(out_path)
}
})
.buffer_unordered(num_cpus::get()) // try not to explode computers
.try_collect()
.await?;
println!("ā
Evaluated {} installable(s)!", out_paths.len());
Ok(out_paths)
}
#[allow(clippy::cast_precision_loss)]
#[instrument(skip(store_paths))]
async fn check_store_paths(
binary_caches: &Vec<String>,
store_paths: &Vec<String>,
show_missing: bool,
) -> Result<()> {
let num_store_paths = store_paths.len();
println!("š”ļø Checking for {num_store_paths} store path(s) in: {binary_caches:?}");
let http = <http::Client as http::Ext>::default();
let progress_bar = ProgressBar::new(num_store_paths as u64).with_style(progress_style()?);
let uncached_paths: Vec<&str> = stream::iter(store_paths)
// Check the cache for all of our paths
.map(|store_path| {
let http = &http;
let progress_bar = &progress_bar;
async move {
let mut has_store_path = false;
for binary_cache in binary_caches {
if http.has_store_path(binary_cache, store_path).await? {
has_store_path = true;
}
}
progress_bar.inc(1);
anyhow::Ok((has_store_path, store_path.as_str()))
}
})
.buffer_unordered(100)
// Filter out misses
.try_filter_map(|(has_store_path, store_path)| async move {
Ok((!has_store_path).then_some(store_path))
})
.try_collect()
.await?;
let num_uncached = uncached_paths.len();
let num_cached = num_store_paths - num_uncached;
println!(
"āļø {:.2}% of paths available ({} out of {})",
(num_cached as f32 / num_store_paths as f32) * 100.0,
num_cached,
num_store_paths,
);
if show_missing {
println!(
"\nāļø Found {num_uncached} uncached paths:\n{}",
uncached_paths.join("\n")
);
}
Ok(())
}
pub fn progress_style() -> Result<ProgressStyle> {
Ok(
ProgressStyle::with_template("[{elapsed_precise}] {bar:40} {pos:>7}/{len:7} {msg}")?
.progress_chars("##-"),
)
}
|