mirror of
				https://github.com/astral-sh/uv.git
				synced 2025-11-04 05:34:28 +00:00 
			
		
		
		
	Concurrent progress bars (#3252)
## Summary
Implements concurrent progress bars. Resolves
https://github.com/astral-sh/uv/issues/1209.
## Test Plan
b21bdfbb-8817-4873-a65c-16c9e8c7c460
			
			
This commit is contained in:
		
							parent
							
								
									70cbc32565
								
							
						
					
					
						commit
						7dc322665c
					
				
					 5 changed files with 477 additions and 230 deletions
				
			
		| 
						 | 
					@ -1,12 +1,14 @@
 | 
				
			||||||
use std::future::Future;
 | 
					use std::future::Future;
 | 
				
			||||||
use std::io;
 | 
					use std::io;
 | 
				
			||||||
use std::path::Path;
 | 
					use std::path::Path;
 | 
				
			||||||
 | 
					use std::pin::Pin;
 | 
				
			||||||
use std::rc::Rc;
 | 
					use std::rc::Rc;
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					use std::task::{Context, Poll};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use futures::{FutureExt, TryStreamExt};
 | 
					use futures::{FutureExt, TryStreamExt};
 | 
				
			||||||
use tempfile::TempDir;
 | 
					use tempfile::TempDir;
 | 
				
			||||||
use tokio::io::AsyncSeekExt;
 | 
					use tokio::io::{AsyncRead, AsyncSeekExt, ReadBuf};
 | 
				
			||||||
use tokio::sync::Semaphore;
 | 
					use tokio::sync::Semaphore;
 | 
				
			||||||
use tokio_util::compat::FuturesAsyncReadCompatExt;
 | 
					use tokio_util::compat::FuturesAsyncReadCompatExt;
 | 
				
			||||||
use tracing::{info_span, instrument, warn, Instrument};
 | 
					use tracing::{info_span, instrument, warn, Instrument};
 | 
				
			||||||
| 
						 | 
					@ -49,6 +51,7 @@ pub struct DistributionDatabase<'a, Context: BuildContext> {
 | 
				
			||||||
    builder: SourceDistributionBuilder<'a, Context>,
 | 
					    builder: SourceDistributionBuilder<'a, Context>,
 | 
				
			||||||
    locks: Rc<Locks>,
 | 
					    locks: Rc<Locks>,
 | 
				
			||||||
    client: ManagedClient<'a>,
 | 
					    client: ManagedClient<'a>,
 | 
				
			||||||
 | 
					    reporter: Option<Arc<dyn Reporter>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
					impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
| 
						 | 
					@ -62,6 +65,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
            builder: SourceDistributionBuilder::new(build_context),
 | 
					            builder: SourceDistributionBuilder::new(build_context),
 | 
				
			||||||
            locks: Rc::new(Locks::default()),
 | 
					            locks: Rc::new(Locks::default()),
 | 
				
			||||||
            client: ManagedClient::new(client, concurrent_downloads),
 | 
					            client: ManagedClient::new(client, concurrent_downloads),
 | 
				
			||||||
 | 
					            reporter: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +74,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
    pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
 | 
					    pub fn with_reporter(self, reporter: impl Reporter + 'static) -> Self {
 | 
				
			||||||
        let reporter = Arc::new(reporter);
 | 
					        let reporter = Arc::new(reporter);
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
 | 
					            reporter: Some(reporter.clone()),
 | 
				
			||||||
            builder: self.builder.with_reporter(reporter),
 | 
					            builder: self.builder.with_reporter(reporter),
 | 
				
			||||||
            ..self
 | 
					            ..self
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -168,6 +173,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
            NoBinary::All => true,
 | 
					            NoBinary::All => true,
 | 
				
			||||||
            NoBinary::Packages(packages) => packages.contains(dist.name()),
 | 
					            NoBinary::Packages(packages) => packages.contains(dist.name()),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if no_binary {
 | 
					        if no_binary {
 | 
				
			||||||
            return Err(Error::NoBinary);
 | 
					            return Err(Error::NoBinary);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -188,6 +194,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
                            WheelCache::Index(&wheel.index).wheel_dir(wheel.name().as_ref()),
 | 
					                            WheelCache::Index(&wheel.index).wheel_dir(wheel.name().as_ref()),
 | 
				
			||||||
                            wheel.filename.stem(),
 | 
					                            wheel.filename.stem(),
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        return self
 | 
					                        return self
 | 
				
			||||||
                            .load_wheel(path, &wheel.filename, cache_entry, dist, hashes)
 | 
					                            .load_wheel(path, &wheel.filename, cache_entry, dist, hashes)
 | 
				
			||||||
                            .await;
 | 
					                            .await;
 | 
				
			||||||
| 
						 | 
					@ -203,7 +210,14 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Download and unzip.
 | 
					                // Download and unzip.
 | 
				
			||||||
                match self
 | 
					                match self
 | 
				
			||||||
                    .stream_wheel(url.clone(), &wheel.filename, &wheel_entry, dist, hashes)
 | 
					                    .stream_wheel(
 | 
				
			||||||
 | 
					                        url.clone(),
 | 
				
			||||||
 | 
					                        &wheel.filename,
 | 
				
			||||||
 | 
					                        wheel.file.size,
 | 
				
			||||||
 | 
					                        &wheel_entry,
 | 
				
			||||||
 | 
					                        dist,
 | 
				
			||||||
 | 
					                        hashes,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
                    .await
 | 
					                    .await
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Ok(archive) => Ok(LocalWheel {
 | 
					                    Ok(archive) => Ok(LocalWheel {
 | 
				
			||||||
| 
						 | 
					@ -220,8 +234,16 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
                        // If the request failed because streaming is unsupported, download the
 | 
					                        // If the request failed because streaming is unsupported, download the
 | 
				
			||||||
                        // wheel directly.
 | 
					                        // wheel directly.
 | 
				
			||||||
                        let archive = self
 | 
					                        let archive = self
 | 
				
			||||||
                            .download_wheel(url, &wheel.filename, &wheel_entry, dist, hashes)
 | 
					                            .download_wheel(
 | 
				
			||||||
 | 
					                                url,
 | 
				
			||||||
 | 
					                                &wheel.filename,
 | 
				
			||||||
 | 
					                                wheel.file.size,
 | 
				
			||||||
 | 
					                                &wheel_entry,
 | 
				
			||||||
 | 
					                                dist,
 | 
				
			||||||
 | 
					                                hashes,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
                            .await?;
 | 
					                            .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        Ok(LocalWheel {
 | 
					                        Ok(LocalWheel {
 | 
				
			||||||
                            dist: Dist::Built(dist.clone()),
 | 
					                            dist: Dist::Built(dist.clone()),
 | 
				
			||||||
                            archive: self.build_context.cache().archive(&archive.id),
 | 
					                            archive: self.build_context.cache().archive(&archive.id),
 | 
				
			||||||
| 
						 | 
					@ -246,6 +268,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
                    .stream_wheel(
 | 
					                    .stream_wheel(
 | 
				
			||||||
                        wheel.url.raw().clone(),
 | 
					                        wheel.url.raw().clone(),
 | 
				
			||||||
                        &wheel.filename,
 | 
					                        &wheel.filename,
 | 
				
			||||||
 | 
					                        None,
 | 
				
			||||||
                        &wheel_entry,
 | 
					                        &wheel_entry,
 | 
				
			||||||
                        dist,
 | 
					                        dist,
 | 
				
			||||||
                        hashes,
 | 
					                        hashes,
 | 
				
			||||||
| 
						 | 
					@ -269,6 +292,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
                            .download_wheel(
 | 
					                            .download_wheel(
 | 
				
			||||||
                                wheel.url.raw().clone(),
 | 
					                                wheel.url.raw().clone(),
 | 
				
			||||||
                                &wheel.filename,
 | 
					                                &wheel.filename,
 | 
				
			||||||
 | 
					                                None,
 | 
				
			||||||
                                &wheel_entry,
 | 
					                                &wheel_entry,
 | 
				
			||||||
                                dist,
 | 
					                                dist,
 | 
				
			||||||
                                hashes,
 | 
					                                hashes,
 | 
				
			||||||
| 
						 | 
					@ -427,6 +451,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        url: Url,
 | 
					        url: Url,
 | 
				
			||||||
        filename: &WheelFilename,
 | 
					        filename: &WheelFilename,
 | 
				
			||||||
 | 
					        size: Option<u64>,
 | 
				
			||||||
        wheel_entry: &CacheEntry,
 | 
					        wheel_entry: &CacheEntry,
 | 
				
			||||||
        dist: &BuiltDist,
 | 
					        dist: &BuiltDist,
 | 
				
			||||||
        hashes: HashPolicy<'_>,
 | 
					        hashes: HashPolicy<'_>,
 | 
				
			||||||
| 
						 | 
					@ -434,8 +459,19 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
        // Create an entry for the HTTP cache.
 | 
					        // Create an entry for the HTTP cache.
 | 
				
			||||||
        let http_entry = wheel_entry.with_file(format!("{}.http", filename.stem()));
 | 
					        let http_entry = wheel_entry.with_file(format!("{}.http", filename.stem()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Fetch the archive from the cache, or download it if necessary.
 | 
				
			||||||
 | 
					        let req = self.request(url.clone())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Extract the size from the `Content-Length` header, if not provided by the registry.
 | 
				
			||||||
 | 
					        let size = size.or_else(|| content_length(&req));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let download = |response: reqwest::Response| {
 | 
					        let download = |response: reqwest::Response| {
 | 
				
			||||||
            async {
 | 
					            async {
 | 
				
			||||||
 | 
					                let progress = self
 | 
				
			||||||
 | 
					                    .reporter
 | 
				
			||||||
 | 
					                    .as_ref()
 | 
				
			||||||
 | 
					                    .map(|reporter| (reporter, reporter.on_download_start(dist.name(), size)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let reader = response
 | 
					                let reader = response
 | 
				
			||||||
                    .bytes_stream()
 | 
					                    .bytes_stream()
 | 
				
			||||||
                    .map_err(|err| self.handle_response_errors(err))
 | 
					                    .map_err(|err| self.handle_response_errors(err))
 | 
				
			||||||
| 
						 | 
					@ -449,7 +485,16 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
                // Download and unzip the wheel to a temporary directory.
 | 
					                // Download and unzip the wheel to a temporary directory.
 | 
				
			||||||
                let temp_dir = tempfile::tempdir_in(self.build_context.cache().root())
 | 
					                let temp_dir = tempfile::tempdir_in(self.build_context.cache().root())
 | 
				
			||||||
                    .map_err(Error::CacheWrite)?;
 | 
					                    .map_err(Error::CacheWrite)?;
 | 
				
			||||||
                uv_extract::stream::unzip(&mut hasher, temp_dir.path()).await?;
 | 
					
 | 
				
			||||||
 | 
					                match progress {
 | 
				
			||||||
 | 
					                    Some((reporter, progress)) => {
 | 
				
			||||||
 | 
					                        let mut reader = ProgressReader::new(&mut hasher, progress, &**reporter);
 | 
				
			||||||
 | 
					                        uv_extract::stream::unzip(&mut reader, temp_dir.path()).await?;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    None => {
 | 
				
			||||||
 | 
					                        uv_extract::stream::unzip(&mut hasher, temp_dir.path()).await?;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // If necessary, exhaust the reader to compute the hash.
 | 
					                // If necessary, exhaust the reader to compute the hash.
 | 
				
			||||||
                if !hashes.is_none() {
 | 
					                if !hashes.is_none() {
 | 
				
			||||||
| 
						 | 
					@ -464,6 +509,10 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
                    .await
 | 
					                    .await
 | 
				
			||||||
                    .map_err(Error::CacheRead)?;
 | 
					                    .map_err(Error::CacheRead)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some((reporter, progress)) = progress {
 | 
				
			||||||
 | 
					                    reporter.on_download_complete(dist.name(), progress);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Ok(Archive::new(
 | 
					                Ok(Archive::new(
 | 
				
			||||||
                    id,
 | 
					                    id,
 | 
				
			||||||
                    hashers.into_iter().map(HashDigest::from).collect(),
 | 
					                    hashers.into_iter().map(HashDigest::from).collect(),
 | 
				
			||||||
| 
						 | 
					@ -523,6 +572,7 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        url: Url,
 | 
					        url: Url,
 | 
				
			||||||
        filename: &WheelFilename,
 | 
					        filename: &WheelFilename,
 | 
				
			||||||
 | 
					        size: Option<u64>,
 | 
				
			||||||
        wheel_entry: &CacheEntry,
 | 
					        wheel_entry: &CacheEntry,
 | 
				
			||||||
        dist: &BuiltDist,
 | 
					        dist: &BuiltDist,
 | 
				
			||||||
        hashes: HashPolicy<'_>,
 | 
					        hashes: HashPolicy<'_>,
 | 
				
			||||||
| 
						 | 
					@ -530,8 +580,18 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
        // Create an entry for the HTTP cache.
 | 
					        // Create an entry for the HTTP cache.
 | 
				
			||||||
        let http_entry = wheel_entry.with_file(format!("{}.http", filename.stem()));
 | 
					        let http_entry = wheel_entry.with_file(format!("{}.http", filename.stem()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let req = self.request(url.clone())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Extract the size from the `Content-Length` header, if not provided by the registry.
 | 
				
			||||||
 | 
					        let size = size.or_else(|| content_length(&req));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let download = |response: reqwest::Response| {
 | 
					        let download = |response: reqwest::Response| {
 | 
				
			||||||
            async {
 | 
					            async {
 | 
				
			||||||
 | 
					                let progress = self
 | 
				
			||||||
 | 
					                    .reporter
 | 
				
			||||||
 | 
					                    .as_ref()
 | 
				
			||||||
 | 
					                    .map(|reporter| (reporter, reporter.on_download_start(dist.name(), size)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let reader = response
 | 
					                let reader = response
 | 
				
			||||||
                    .bytes_stream()
 | 
					                    .bytes_stream()
 | 
				
			||||||
                    .map_err(|err| self.handle_response_errors(err))
 | 
					                    .map_err(|err| self.handle_response_errors(err))
 | 
				
			||||||
| 
						 | 
					@ -541,9 +601,25 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
                let temp_file = tempfile::tempfile_in(self.build_context.cache().root())
 | 
					                let temp_file = tempfile::tempfile_in(self.build_context.cache().root())
 | 
				
			||||||
                    .map_err(Error::CacheWrite)?;
 | 
					                    .map_err(Error::CacheWrite)?;
 | 
				
			||||||
                let mut writer = tokio::io::BufWriter::new(tokio::fs::File::from_std(temp_file));
 | 
					                let mut writer = tokio::io::BufWriter::new(tokio::fs::File::from_std(temp_file));
 | 
				
			||||||
                tokio::io::copy(&mut reader.compat(), &mut writer)
 | 
					
 | 
				
			||||||
                    .await
 | 
					                match progress {
 | 
				
			||||||
                    .map_err(Error::CacheWrite)?;
 | 
					                    Some((reporter, progress)) => {
 | 
				
			||||||
 | 
					                        // Wrap the reader in a progress reporter. This will report 100% progress
 | 
				
			||||||
 | 
					                        // after the download is complete, even if we still have to unzip and hash
 | 
				
			||||||
 | 
					                        // part of the file.
 | 
				
			||||||
 | 
					                        let mut reader =
 | 
				
			||||||
 | 
					                            ProgressReader::new(reader.compat(), progress, &**reporter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        tokio::io::copy(&mut reader, &mut writer)
 | 
				
			||||||
 | 
					                            .await
 | 
				
			||||||
 | 
					                            .map_err(Error::CacheWrite)?;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    None => {
 | 
				
			||||||
 | 
					                        tokio::io::copy(&mut reader.compat(), &mut writer)
 | 
				
			||||||
 | 
					                            .await
 | 
				
			||||||
 | 
					                            .map_err(Error::CacheWrite)?;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // Unzip the wheel to a temporary directory.
 | 
					                // Unzip the wheel to a temporary directory.
 | 
				
			||||||
                let temp_dir = tempfile::tempdir_in(self.build_context.cache().root())
 | 
					                let temp_dir = tempfile::tempdir_in(self.build_context.cache().root())
 | 
				
			||||||
| 
						 | 
					@ -588,6 +664,10 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
 | 
				
			||||||
                    .await
 | 
					                    .await
 | 
				
			||||||
                    .map_err(Error::CacheRead)?;
 | 
					                    .map_err(Error::CacheRead)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Some((reporter, progress)) = progress {
 | 
				
			||||||
 | 
					                    reporter.on_download_complete(dist.name(), progress);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Ok(Archive::new(id, hashes))
 | 
					                Ok(Archive::new(id, hashes))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            .instrument(info_span!("wheel", wheel = %dist))
 | 
					            .instrument(info_span!("wheel", wheel = %dist))
 | 
				
			||||||
| 
						 | 
					@ -813,6 +893,50 @@ impl<'a> ManagedClient<'a> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Returns the value of the `Content-Length` header from the [`reqwest::Request`], if present.
 | 
				
			||||||
 | 
					fn content_length(req: &reqwest::Request) -> Option<u64> {
 | 
				
			||||||
 | 
					    req.headers()
 | 
				
			||||||
 | 
					        .get(reqwest::header::CONTENT_LENGTH)
 | 
				
			||||||
 | 
					        .and_then(|val| val.to_str().ok())
 | 
				
			||||||
 | 
					        .and_then(|val| val.parse::<u64>().ok())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// An asynchronous reader that reports progress as bytes are read.
 | 
				
			||||||
 | 
					struct ProgressReader<'a, R> {
 | 
				
			||||||
 | 
					    reader: R,
 | 
				
			||||||
 | 
					    index: usize,
 | 
				
			||||||
 | 
					    reporter: &'a dyn Reporter,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a, R> ProgressReader<'a, R> {
 | 
				
			||||||
 | 
					    /// Create a new [`ProgressReader`] that wraps another reader.
 | 
				
			||||||
 | 
					    fn new(reader: R, index: usize, reporter: &'a dyn Reporter) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            reader,
 | 
				
			||||||
 | 
					            index,
 | 
				
			||||||
 | 
					            reporter,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<R> AsyncRead for ProgressReader<'_, R>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    R: AsyncRead + Unpin,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    fn poll_read(
 | 
				
			||||||
 | 
					        mut self: Pin<&mut Self>,
 | 
				
			||||||
 | 
					        cx: &mut Context<'_>,
 | 
				
			||||||
 | 
					        buf: &mut ReadBuf<'_>,
 | 
				
			||||||
 | 
					    ) -> Poll<io::Result<()>> {
 | 
				
			||||||
 | 
					        Pin::new(&mut self.as_mut().reader)
 | 
				
			||||||
 | 
					            .poll_read(cx, buf)
 | 
				
			||||||
 | 
					            .map_ok(|()| {
 | 
				
			||||||
 | 
					                self.reporter
 | 
				
			||||||
 | 
					                    .on_download_progress(self.index, buf.filled().len() as u64);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A pointer to an archive in the cache, fetched from an HTTP archive.
 | 
					/// A pointer to an archive in the cache, fetched from an HTTP archive.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Encoded with `MsgPack`, and represented on disk by a `.http` file.
 | 
					/// Encoded with `MsgPack`, and represented on disk by a `.http` file.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ use std::sync::Arc;
 | 
				
			||||||
use url::Url;
 | 
					use url::Url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use distribution_types::BuildableSource;
 | 
					use distribution_types::BuildableSource;
 | 
				
			||||||
 | 
					use pep508_rs::PackageName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait Reporter: Send + Sync {
 | 
					pub trait Reporter: Send + Sync {
 | 
				
			||||||
    /// Callback to invoke when a source distribution build is kicked off.
 | 
					    /// Callback to invoke when a source distribution build is kicked off.
 | 
				
			||||||
| 
						 | 
					@ -15,7 +16,17 @@ pub trait Reporter: Send + Sync {
 | 
				
			||||||
    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize;
 | 
					    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Callback to invoke when a repository checkout completes.
 | 
					    /// Callback to invoke when a repository checkout completes.
 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize);
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download is kicked off.
 | 
				
			||||||
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download makes progress (i.e. some number of bytes are
 | 
				
			||||||
 | 
					    /// downloaded).
 | 
				
			||||||
 | 
					    fn on_download_progress(&self, id: usize, inc: u64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download is complete.
 | 
				
			||||||
 | 
					    fn on_download_complete(&self, name: &PackageName, id: usize);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A facade for converting from [`Reporter`] to [`uv_git::Reporter`].
 | 
					/// A facade for converting from [`Reporter`] to [`uv_git::Reporter`].
 | 
				
			||||||
| 
						 | 
					@ -34,7 +45,7 @@ impl uv_git::Reporter for Facade {
 | 
				
			||||||
        self.reporter.on_checkout_start(url, rev)
 | 
					        self.reporter.on_checkout_start(url, rev)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize) {
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
 | 
				
			||||||
        self.reporter.on_checkout_complete(url, rev, index);
 | 
					        self.reporter.on_checkout_complete(url, rev, id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ use std::path::Path;
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use futures::{stream::FuturesUnordered, FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt};
 | 
					use futures::{stream::FuturesUnordered, FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt};
 | 
				
			||||||
 | 
					use pep508_rs::PackageName;
 | 
				
			||||||
use tokio::task::JoinError;
 | 
					use tokio::task::JoinError;
 | 
				
			||||||
use tracing::instrument;
 | 
					use tracing::instrument;
 | 
				
			||||||
use url::Url;
 | 
					use url::Url;
 | 
				
			||||||
| 
						 | 
					@ -169,6 +170,7 @@ impl<'a, Context: BuildContext> Downloader<'a, Context> {
 | 
				
			||||||
        let id = dist.distribution_id();
 | 
					        let id = dist.distribution_id();
 | 
				
			||||||
        if in_flight.downloads.register(id.clone()) {
 | 
					        if in_flight.downloads.register(id.clone()) {
 | 
				
			||||||
            let policy = self.hashes.get(&dist);
 | 
					            let policy = self.hashes.get(&dist);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let result = self
 | 
					            let result = self
 | 
				
			||||||
                .database
 | 
					                .database
 | 
				
			||||||
                .get_or_build_wheel(&dist, self.tags, policy)
 | 
					                .get_or_build_wheel(&dist, self.tags, policy)
 | 
				
			||||||
| 
						 | 
					@ -223,6 +225,16 @@ pub trait Reporter: Send + Sync {
 | 
				
			||||||
    /// Callback to invoke when the operation is complete.
 | 
					    /// Callback to invoke when the operation is complete.
 | 
				
			||||||
    fn on_complete(&self);
 | 
					    fn on_complete(&self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download is kicked off.
 | 
				
			||||||
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download makes progress (i.e. some number of bytes are
 | 
				
			||||||
 | 
					    /// downloaded).
 | 
				
			||||||
 | 
					    fn on_download_progress(&self, index: usize, bytes: u64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download is complete.
 | 
				
			||||||
 | 
					    fn on_download_complete(&self, name: &PackageName, index: usize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Callback to invoke when a source distribution build is kicked off.
 | 
					    /// Callback to invoke when a source distribution build is kicked off.
 | 
				
			||||||
    fn on_build_start(&self, source: &BuildableSource) -> usize;
 | 
					    fn on_build_start(&self, source: &BuildableSource) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -269,4 +281,16 @@ impl uv_distribution::Reporter for Facade {
 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize) {
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize) {
 | 
				
			||||||
        self.reporter.on_checkout_complete(url, rev, index);
 | 
					        self.reporter.on_checkout_complete(url, rev, index);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_download_start(name, size)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_progress(&self, index: usize, inc: u64) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_progress(index, inc);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_complete(&self, name: &PackageName, index: usize) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_complete(name, index);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,11 +20,21 @@ pub trait Reporter: Send + Sync {
 | 
				
			||||||
    /// Callback to invoke when a source distribution build is complete.
 | 
					    /// Callback to invoke when a source distribution build is complete.
 | 
				
			||||||
    fn on_build_complete(&self, source: &BuildableSource, id: usize);
 | 
					    fn on_build_complete(&self, source: &BuildableSource, id: usize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download is kicked off.
 | 
				
			||||||
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download makes progress (i.e. some number of bytes are
 | 
				
			||||||
 | 
					    /// downloaded).
 | 
				
			||||||
 | 
					    fn on_download_progress(&self, id: usize, bytes: u64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Callback to invoke when a download is complete.
 | 
				
			||||||
 | 
					    fn on_download_complete(&self, name: &PackageName, id: usize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Callback to invoke when a repository checkout begins.
 | 
					    /// Callback to invoke when a repository checkout begins.
 | 
				
			||||||
    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize;
 | 
					    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Callback to invoke when a repository checkout completes.
 | 
					    /// Callback to invoke when a repository checkout completes.
 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize);
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A facade for converting from [`Reporter`] to [`uv_distribution::Reporter`].
 | 
					/// A facade for converting from [`Reporter`] to [`uv_distribution::Reporter`].
 | 
				
			||||||
| 
						 | 
					@ -45,7 +55,19 @@ impl uv_distribution::Reporter for Facade {
 | 
				
			||||||
        self.reporter.on_checkout_start(url, rev)
 | 
					        self.reporter.on_checkout_start(url, rev)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize) {
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
 | 
				
			||||||
        self.reporter.on_checkout_complete(url, rev, index);
 | 
					        self.reporter.on_checkout_complete(url, rev, id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_download_start(name, size)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_progress(&self, id: usize, bytes: u64) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_progress(id, bytes);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_complete(&self, name: &PackageName, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_complete(name, id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ use std::time::Duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
 | 
					use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
 | 
				
			||||||
use owo_colors::OwoColorize;
 | 
					use owo_colors::OwoColorize;
 | 
				
			||||||
 | 
					use rustc_hash::FxHashMap;
 | 
				
			||||||
use url::Url;
 | 
					use url::Url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use distribution_types::{
 | 
					use distribution_types::{
 | 
				
			||||||
| 
						 | 
					@ -14,91 +15,109 @@ use uv_normalize::PackageName;
 | 
				
			||||||
use crate::printer::Printer;
 | 
					use crate::printer::Printer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub(crate) struct DownloadReporter {
 | 
					struct ProgressReporter {
 | 
				
			||||||
    printer: Printer,
 | 
					    printer: Printer,
 | 
				
			||||||
 | 
					    root: ProgressBar,
 | 
				
			||||||
    multi_progress: MultiProgress,
 | 
					    multi_progress: MultiProgress,
 | 
				
			||||||
    progress: ProgressBar,
 | 
					    state: Arc<Mutex<BarState>>,
 | 
				
			||||||
    bars: Arc<Mutex<Vec<ProgressBar>>>,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<Printer> for DownloadReporter {
 | 
					#[derive(Default, Debug)]
 | 
				
			||||||
    fn from(printer: Printer) -> Self {
 | 
					struct BarState {
 | 
				
			||||||
        let multi_progress = MultiProgress::with_draw_target(printer.target());
 | 
					    // The number of bars that precede any download bars (i.e. build/checkout status).
 | 
				
			||||||
 | 
					    headers: usize,
 | 
				
			||||||
 | 
					    // A list of donwnload bar sizes, in descending order.
 | 
				
			||||||
 | 
					    sizes: Vec<u64>,
 | 
				
			||||||
 | 
					    // A map of progress bars, by ID.
 | 
				
			||||||
 | 
					    bars: FxHashMap<usize, ProgressBar>,
 | 
				
			||||||
 | 
					    // A monotonic counter for bar IDs.
 | 
				
			||||||
 | 
					    id: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let progress = multi_progress.add(ProgressBar::with_draw_target(None, printer.target()));
 | 
					impl BarState {
 | 
				
			||||||
        progress.set_style(
 | 
					    // Returns a unique ID for a new bar.
 | 
				
			||||||
            ProgressStyle::with_template("{bar:20} [{pos}/{len}] {wide_msg:.dim}").unwrap(),
 | 
					    fn id(&mut self) -> usize {
 | 
				
			||||||
        );
 | 
					        self.id += 1;
 | 
				
			||||||
        progress.set_message("Fetching packages...");
 | 
					        self.id
 | 
				
			||||||
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            printer,
 | 
					 | 
				
			||||||
            multi_progress,
 | 
					 | 
				
			||||||
            progress,
 | 
					 | 
				
			||||||
            bars: Arc::new(Mutex::new(Vec::new())),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl DownloadReporter {
 | 
					impl ProgressReporter {
 | 
				
			||||||
    #[must_use]
 | 
					 | 
				
			||||||
    pub(crate) fn with_length(self, length: u64) -> Self {
 | 
					 | 
				
			||||||
        self.progress.set_length(length);
 | 
					 | 
				
			||||||
        self
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl DownloadReporter {
 | 
					 | 
				
			||||||
    fn on_any_build_start(&self, color_string: &str) -> usize {
 | 
					    fn on_any_build_start(&self, color_string: &str) -> usize {
 | 
				
			||||||
 | 
					        let mut state = self.state.lock().unwrap();
 | 
				
			||||||
 | 
					        let id = state.id();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let progress = self.multi_progress.insert_before(
 | 
					        let progress = self.multi_progress.insert_before(
 | 
				
			||||||
            &self.progress,
 | 
					            &self.root,
 | 
				
			||||||
            ProgressBar::with_draw_target(None, self.printer.target()),
 | 
					            ProgressBar::with_draw_target(None, self.printer.target()),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        progress.set_style(ProgressStyle::with_template("{wide_msg}").unwrap());
 | 
					        progress.set_style(ProgressStyle::with_template("{wide_msg}").unwrap());
 | 
				
			||||||
        progress.set_message(format!("{} {}", "Building".bold().cyan(), color_string));
 | 
					        progress.set_message(format!("{} {}", "Building".bold().cyan(), color_string));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut bars = self.bars.lock().unwrap();
 | 
					        state.headers += 1;
 | 
				
			||||||
        bars.push(progress);
 | 
					        state.bars.insert(id, progress);
 | 
				
			||||||
        bars.len() - 1
 | 
					        id
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn on_any_build_complete(&self, color_string: &str, id: usize) {
 | 
					    fn on_any_build_complete(&self, color_string: &str, id: usize) {
 | 
				
			||||||
        let bars = self.bars.lock().unwrap();
 | 
					        let progress = {
 | 
				
			||||||
        let progress = &bars[id];
 | 
					            let mut state = self.state.lock().unwrap();
 | 
				
			||||||
 | 
					            state.headers -= 1;
 | 
				
			||||||
 | 
					            state.bars.remove(&id).unwrap()
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        progress.finish_with_message(format!("   {} {}", "Built".bold().green(), color_string));
 | 
					        progress.finish_with_message(format!("   {} {}", "Built".bold().green(), color_string));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl uv_installer::DownloadReporter for DownloadReporter {
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize {
 | 
				
			||||||
    fn on_progress(&self, dist: &CachedDist) {
 | 
					        let mut state = self.state.lock().unwrap();
 | 
				
			||||||
        self.progress.set_message(format!("{dist}"));
 | 
					
 | 
				
			||||||
        self.progress.inc(1);
 | 
					        // Preserve ascending order.
 | 
				
			||||||
 | 
					        let position = size.map_or(0, |size| state.sizes.partition_point(|&len| len < size));
 | 
				
			||||||
 | 
					        state.sizes.insert(position, size.unwrap_or(0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let progress = self.multi_progress.insert(
 | 
				
			||||||
 | 
					            // Make sure not to reorder the initial "Downloading..." bar, or any previous bars.
 | 
				
			||||||
 | 
					            position + 1 + state.headers,
 | 
				
			||||||
 | 
					            ProgressBar::with_draw_target(size, self.printer.target()),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if size.is_some() {
 | 
				
			||||||
 | 
					            progress.set_style(
 | 
				
			||||||
 | 
					                ProgressStyle::with_template(
 | 
				
			||||||
 | 
					                    "{msg:10.dim} {bar:30.green/dim} {decimal_bytes:>7}/{decimal_total_bytes:7}",
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					                .progress_chars("--"),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            progress.set_message(name.to_string());
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            progress.set_style(ProgressStyle::with_template("{wide_msg:.dim} ....").unwrap());
 | 
				
			||||||
 | 
					            progress.set_message(name.to_string());
 | 
				
			||||||
 | 
					            progress.finish();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let id = state.id();
 | 
				
			||||||
 | 
					        state.bars.insert(id, progress);
 | 
				
			||||||
 | 
					        id
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn on_complete(&self) {
 | 
					    fn on_download_progress(&self, id: usize, bytes: u64) {
 | 
				
			||||||
        self.progress.finish_and_clear();
 | 
					        self.state.lock().unwrap().bars[&id].inc(bytes);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn on_build_start(&self, source: &BuildableSource) -> usize {
 | 
					    fn on_download_complete(&self, id: usize) {
 | 
				
			||||||
        self.on_any_build_start(&source.to_color_string())
 | 
					        let progress = self.state.lock().unwrap().bars.remove(&id).unwrap();
 | 
				
			||||||
    }
 | 
					        progress.finish_and_clear();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_build_complete(&self, source: &BuildableSource, index: usize) {
 | 
					 | 
				
			||||||
        self.on_any_build_complete(&source.to_color_string(), index);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_editable_build_start(&self, dist: &LocalEditable) -> usize {
 | 
					 | 
				
			||||||
        self.on_any_build_start(&dist.to_color_string())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_editable_build_complete(&self, dist: &LocalEditable, id: usize) {
 | 
					 | 
				
			||||||
        self.on_any_build_complete(&dist.to_color_string(), id);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
 | 
					    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
 | 
				
			||||||
 | 
					        let mut state = self.state.lock().unwrap();
 | 
				
			||||||
 | 
					        let id = state.id();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let progress = self.multi_progress.insert_before(
 | 
					        let progress = self.multi_progress.insert_before(
 | 
				
			||||||
            &self.progress,
 | 
					            &self.root,
 | 
				
			||||||
            ProgressBar::with_draw_target(None, self.printer.target()),
 | 
					            ProgressBar::with_draw_target(None, self.printer.target()),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,14 +130,18 @@ impl uv_installer::DownloadReporter for DownloadReporter {
 | 
				
			||||||
        ));
 | 
					        ));
 | 
				
			||||||
        progress.finish();
 | 
					        progress.finish();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut bars = self.bars.lock().unwrap();
 | 
					        state.headers += 1;
 | 
				
			||||||
        bars.push(progress);
 | 
					        state.bars.insert(id, progress);
 | 
				
			||||||
        bars.len() - 1
 | 
					        id
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize) {
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
 | 
				
			||||||
        let bars = self.bars.lock().unwrap();
 | 
					        let progress = {
 | 
				
			||||||
        let progress = &bars[index];
 | 
					            let mut state = self.state.lock().unwrap();
 | 
				
			||||||
 | 
					            state.headers -= 1;
 | 
				
			||||||
 | 
					            state.bars.remove(&id).unwrap()
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        progress.finish_with_message(format!(
 | 
					        progress.finish_with_message(format!(
 | 
				
			||||||
            " {} {} ({})",
 | 
					            " {} {} ({})",
 | 
				
			||||||
            "Updated".bold().green(),
 | 
					            "Updated".bold().green(),
 | 
				
			||||||
| 
						 | 
					@ -128,6 +151,205 @@ impl uv_installer::DownloadReporter for DownloadReporter {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub(crate) struct DownloadReporter {
 | 
				
			||||||
 | 
					    reporter: ProgressReporter,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<Printer> for DownloadReporter {
 | 
				
			||||||
 | 
					    fn from(printer: Printer) -> Self {
 | 
				
			||||||
 | 
					        let multi_progress = MultiProgress::with_draw_target(printer.target());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let progress = multi_progress.add(ProgressBar::with_draw_target(None, printer.target()));
 | 
				
			||||||
 | 
					        progress.enable_steady_tick(Duration::from_millis(200));
 | 
				
			||||||
 | 
					        progress.set_style(
 | 
				
			||||||
 | 
					            ProgressStyle::with_template("{spinner:.white} {msg:.dim} ({pos}/{len})")
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					                .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        progress.set_message("Downloading packages...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let reporter = ProgressReporter {
 | 
				
			||||||
 | 
					            printer,
 | 
				
			||||||
 | 
					            multi_progress,
 | 
				
			||||||
 | 
					            root: progress,
 | 
				
			||||||
 | 
					            state: Arc::default(),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self { reporter }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl DownloadReporter {
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub(crate) fn with_length(self, length: u64) -> Self {
 | 
				
			||||||
 | 
					        self.reporter.root.set_length(length);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl uv_installer::DownloadReporter for DownloadReporter {
 | 
				
			||||||
 | 
					    fn on_progress(&self, _dist: &CachedDist) {
 | 
				
			||||||
 | 
					        self.reporter.root.inc(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_complete(&self) {
 | 
				
			||||||
 | 
					        self.reporter.root.finish_and_clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_build_start(&self, source: &BuildableSource) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_any_build_start(&source.to_color_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_build_complete(&self, source: &BuildableSource, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter
 | 
				
			||||||
 | 
					            .on_any_build_complete(&source.to_color_string(), id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_editable_build_start(&self, dist: &LocalEditable) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_any_build_start(&dist.to_color_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_editable_build_complete(&self, dist: &LocalEditable, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter
 | 
				
			||||||
 | 
					            .on_any_build_complete(&dist.to_color_string(), id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_download_start(name, size)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_progress(&self, id: usize, bytes: u64) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_progress(id, bytes);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_complete(&self, _name: &PackageName, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_complete(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_checkout_start(url, rev)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter.on_checkout_complete(url, rev, id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub(crate) struct ResolverReporter {
 | 
				
			||||||
 | 
					    reporter: ProgressReporter,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ResolverReporter {
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub(crate) fn with_length(self, length: u64) -> Self {
 | 
				
			||||||
 | 
					        self.reporter.root.set_length(length);
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<Printer> for ResolverReporter {
 | 
				
			||||||
 | 
					    fn from(printer: Printer) -> Self {
 | 
				
			||||||
 | 
					        let multi_progress = MultiProgress::with_draw_target(printer.target());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let root = multi_progress.add(ProgressBar::with_draw_target(None, printer.target()));
 | 
				
			||||||
 | 
					        root.enable_steady_tick(Duration::from_millis(200));
 | 
				
			||||||
 | 
					        root.set_style(
 | 
				
			||||||
 | 
					            ProgressStyle::with_template("{spinner:.white} {wide_msg:.dim}")
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					                .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        root.set_message("Resolving dependencies...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let reporter = ProgressReporter {
 | 
				
			||||||
 | 
					            root,
 | 
				
			||||||
 | 
					            printer,
 | 
				
			||||||
 | 
					            multi_progress,
 | 
				
			||||||
 | 
					            state: Arc::default(),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ResolverReporter { reporter }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl uv_resolver::ResolverReporter for ResolverReporter {
 | 
				
			||||||
 | 
					    fn on_progress(&self, name: &PackageName, version_or_url: &VersionOrUrlRef) {
 | 
				
			||||||
 | 
					        match version_or_url {
 | 
				
			||||||
 | 
					            VersionOrUrlRef::Version(version) => {
 | 
				
			||||||
 | 
					                self.reporter.root.set_message(format!("{name}=={version}"));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            VersionOrUrlRef::Url(url) => {
 | 
				
			||||||
 | 
					                self.reporter.root.set_message(format!("{name} @ {url}"));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_complete(&self) {
 | 
				
			||||||
 | 
					        self.reporter.root.finish_and_clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_build_start(&self, source: &BuildableSource) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_any_build_start(&source.to_color_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_build_complete(&self, source: &BuildableSource, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter
 | 
				
			||||||
 | 
					            .on_any_build_complete(&source.to_color_string(), id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_checkout_start(url, rev)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter.on_checkout_complete(url, rev, id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_download_start(name, size)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_progress(&self, id: usize, bytes: u64) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_progress(id, bytes);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_complete(&self, _name: &PackageName, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_complete(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl uv_distribution::Reporter for ResolverReporter {
 | 
				
			||||||
 | 
					    fn on_build_start(&self, source: &BuildableSource) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_any_build_start(&source.to_color_string())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_build_complete(&self, source: &BuildableSource, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter
 | 
				
			||||||
 | 
					            .on_any_build_complete(&source.to_color_string(), id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_start(&self, name: &PackageName, size: Option<u64>) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_download_start(name, size)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_progress(&self, id: usize, bytes: u64) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_progress(id, bytes);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_download_complete(&self, _name: &PackageName, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter.on_download_complete(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
 | 
				
			||||||
 | 
					        self.reporter.on_checkout_start(url, rev)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn on_checkout_complete(&self, url: &Url, rev: &str, id: usize) {
 | 
				
			||||||
 | 
					        self.reporter.on_checkout_complete(url, rev, id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub(crate) struct InstallReporter {
 | 
					pub(crate) struct InstallReporter {
 | 
				
			||||||
    progress: ProgressBar,
 | 
					    progress: ProgressBar,
 | 
				
			||||||
| 
						 | 
					@ -163,162 +385,6 @@ impl uv_installer::InstallReporter for InstallReporter {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
pub(crate) struct ResolverReporter {
 | 
					 | 
				
			||||||
    printer: Printer,
 | 
					 | 
				
			||||||
    multi_progress: MultiProgress,
 | 
					 | 
				
			||||||
    progress: ProgressBar,
 | 
					 | 
				
			||||||
    bars: Arc<Mutex<Vec<ProgressBar>>>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl From<Printer> for ResolverReporter {
 | 
					 | 
				
			||||||
    fn from(printer: Printer) -> Self {
 | 
					 | 
				
			||||||
        let multi_progress = MultiProgress::with_draw_target(printer.target());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let progress = multi_progress.add(ProgressBar::with_draw_target(None, printer.target()));
 | 
					 | 
				
			||||||
        progress.enable_steady_tick(Duration::from_millis(200));
 | 
					 | 
				
			||||||
        progress.set_style(
 | 
					 | 
				
			||||||
            ProgressStyle::with_template("{spinner:.white} {wide_msg:.dim}")
 | 
					 | 
				
			||||||
                .unwrap()
 | 
					 | 
				
			||||||
                .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        progress.set_message("Resolving dependencies...");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            printer,
 | 
					 | 
				
			||||||
            multi_progress,
 | 
					 | 
				
			||||||
            progress,
 | 
					 | 
				
			||||||
            bars: Arc::new(Mutex::new(Vec::new())),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl ResolverReporter {
 | 
					 | 
				
			||||||
    #[must_use]
 | 
					 | 
				
			||||||
    pub(crate) fn with_length(self, length: u64) -> Self {
 | 
					 | 
				
			||||||
        self.progress.set_length(length);
 | 
					 | 
				
			||||||
        self
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_progress(&self, name: &PackageName, version_or_url: &VersionOrUrlRef) {
 | 
					 | 
				
			||||||
        match version_or_url {
 | 
					 | 
				
			||||||
            VersionOrUrlRef::Version(version) => {
 | 
					 | 
				
			||||||
                self.progress.set_message(format!("{name}=={version}"));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            VersionOrUrlRef::Url(url) => {
 | 
					 | 
				
			||||||
                self.progress.set_message(format!("{name} @ {url}"));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_complete(&self) {
 | 
					 | 
				
			||||||
        self.progress.finish_and_clear();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_build_start(&self, source: &BuildableSource) -> usize {
 | 
					 | 
				
			||||||
        let progress = self.multi_progress.insert_before(
 | 
					 | 
				
			||||||
            &self.progress,
 | 
					 | 
				
			||||||
            ProgressBar::with_draw_target(None, self.printer.target()),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        progress.set_style(ProgressStyle::with_template("{wide_msg}").unwrap());
 | 
					 | 
				
			||||||
        progress.set_message(format!(
 | 
					 | 
				
			||||||
            "{} {}",
 | 
					 | 
				
			||||||
            "Building".bold().cyan(),
 | 
					 | 
				
			||||||
            source.to_color_string(),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut bars = self.bars.lock().unwrap();
 | 
					 | 
				
			||||||
        bars.push(progress);
 | 
					 | 
				
			||||||
        bars.len() - 1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_build_complete(&self, source: &BuildableSource, index: usize) {
 | 
					 | 
				
			||||||
        let bars = self.bars.lock().unwrap();
 | 
					 | 
				
			||||||
        let progress = &bars[index];
 | 
					 | 
				
			||||||
        progress.finish_with_message(format!(
 | 
					 | 
				
			||||||
            "   {} {}",
 | 
					 | 
				
			||||||
            "Built".bold().green(),
 | 
					 | 
				
			||||||
            source.to_color_string(),
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
 | 
					 | 
				
			||||||
        let progress = self.multi_progress.insert_before(
 | 
					 | 
				
			||||||
            &self.progress,
 | 
					 | 
				
			||||||
            ProgressBar::with_draw_target(None, self.printer.target()),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        progress.set_style(ProgressStyle::with_template("{wide_msg}").unwrap());
 | 
					 | 
				
			||||||
        progress.set_message(format!(
 | 
					 | 
				
			||||||
            "{} {} ({})",
 | 
					 | 
				
			||||||
            "Updating".bold().cyan(),
 | 
					 | 
				
			||||||
            url,
 | 
					 | 
				
			||||||
            rev.dimmed()
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
        progress.finish();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut bars = self.bars.lock().unwrap();
 | 
					 | 
				
			||||||
        bars.push(progress);
 | 
					 | 
				
			||||||
        bars.len() - 1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize) {
 | 
					 | 
				
			||||||
        let bars = self.bars.lock().unwrap();
 | 
					 | 
				
			||||||
        let progress = &bars[index];
 | 
					 | 
				
			||||||
        progress.finish_with_message(format!(
 | 
					 | 
				
			||||||
            " {} {} ({})",
 | 
					 | 
				
			||||||
            "Updated".bold().green(),
 | 
					 | 
				
			||||||
            url,
 | 
					 | 
				
			||||||
            rev.dimmed()
 | 
					 | 
				
			||||||
        ));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl uv_resolver::ResolverReporter for ResolverReporter {
 | 
					 | 
				
			||||||
    fn on_progress(&self, name: &PackageName, version_or_url: &VersionOrUrlRef) {
 | 
					 | 
				
			||||||
        self.on_progress(name, version_or_url);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_complete(&self) {
 | 
					 | 
				
			||||||
        self.on_complete();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_build_start(&self, source: &BuildableSource) -> usize {
 | 
					 | 
				
			||||||
        self.on_build_start(source)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_build_complete(&self, source: &BuildableSource, index: usize) {
 | 
					 | 
				
			||||||
        self.on_build_complete(source, index);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
 | 
					 | 
				
			||||||
        self.on_checkout_start(url, rev)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize) {
 | 
					 | 
				
			||||||
        self.on_checkout_complete(url, rev, index);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl uv_distribution::Reporter for ResolverReporter {
 | 
					 | 
				
			||||||
    fn on_build_start(&self, source: &BuildableSource) -> usize {
 | 
					 | 
				
			||||||
        self.on_build_start(source)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_build_complete(&self, source: &BuildableSource, index: usize) {
 | 
					 | 
				
			||||||
        self.on_build_complete(source, index);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_checkout_start(&self, url: &Url, rev: &str) -> usize {
 | 
					 | 
				
			||||||
        self.on_checkout_start(url, rev)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize) {
 | 
					 | 
				
			||||||
        self.on_checkout_complete(url, rev, index);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Like [`std::fmt::Display`], but with colors.
 | 
					/// Like [`std::fmt::Display`], but with colors.
 | 
				
			||||||
trait ColorDisplay {
 | 
					trait ColorDisplay {
 | 
				
			||||||
    fn to_color_string(&self) -> String;
 | 
					    fn to_color_string(&self) -> String;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue