From cc183fccca73df619c78dd0ca2567ac547c56ad2 Mon Sep 17 00:00:00 2001 From: seth Date: Sun, 8 Sep 2024 23:39:48 -0400 Subject: feat: initial commit --- src/command.rs | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/command.rs (limited to 'src/command.rs') diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..ef3461e --- /dev/null +++ b/src/command.rs @@ -0,0 +1,123 @@ +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() { + installables_paths(installables).await? + } else if let Some(configuration) = &self.configuration { + configuration_paths(configuration)? + } else { + nix::all_flake_installables(&self.flake)? + }; + + check_store_paths(&self.binary_cache, &store_paths, self.show_missing).await?; + + Ok(()) + } +} + +#[instrument(skip(installables))] +async fn installables_paths(installables: Vec) -> Result> { + println!( + "šŸ”ƒ Attempting to evaluating {} 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 = 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) +} + +#[instrument(skip(configuration_ref))] +fn configuration_paths(configuration_ref: &str) -> Result> { + println!("ā“ Indexing requisites of configuration closure"); + let closure_paths = nix::configuration_closure_paths(configuration_ref)?; + Ok(closure_paths) +} + +#[allow(clippy::cast_precision_loss)] +#[instrument(skip(store_paths))] +async fn check_store_paths( + binary_cache: &str, + store_paths: &Vec, + show_missing: bool, +) -> Result<()> { + let num_store_paths = store_paths.len(); + println!("šŸŒ”ļø Checking for {num_store_paths} store path(s) in {binary_cache}",); + + let http = ::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 has_store_path = http.has_store_path(binary_cache, store_path).await?; + 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 { + Ok( + ProgressStyle::with_template("[{elapsed_precise}] {bar:40} {pos:>7}/{len:7} {msg}")? + .progress_chars("##-"), + ) +} -- cgit v1.2.3