1use std::cell::{Cell, Ref, RefCell, RefMut};
2use std::cmp::Ordering;
3use std::collections::{BTreeSet, HashMap, HashSet};
4use std::fmt;
5use std::hash;
6use std::mem;
7use std::path::{Path, PathBuf};
8use std::time::{Duration, Instant};
9
10use anyhow::Context;
11use bytesize::ByteSize;
12use curl::easy::{Easy, HttpVersion};
13use curl::multi::{EasyHandle, Multi};
14use lazycell::LazyCell;
15use log::{debug, warn};
16use semver::Version;
17use serde::ser;
18use serde::Serialize;
19
20use crate::core::compiler::{CompileKind, RustcTargetData};
21use crate::core::dependency::DepKind;
22use crate::core::interning::InternedString;
23use crate::core::resolver::{HasDevUnits, Resolve};
24use crate::core::source::MaybePackage;
25use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
26use crate::core::{FeatureMap, SourceMap, Summary};
27use crate::ops;
28use crate::util::config::PackageCacheLock;
29use crate::util::errors::{CargoResult, CargoResultExt, HttpNot200};
30use crate::util::network::Retry;
31use crate::util::{self, internal, Config, Progress, ProgressStyle};
32
33#[derive(Clone)]
39pub struct Package {
40 manifest: Manifest,
42 manifest_path: PathBuf,
44}
45
46impl Ord for Package {
47 fn cmp(&self, other: &Package) -> Ordering {
48 self.package_id().cmp(&other.package_id())
49 }
50}
51
52impl PartialOrd for Package {
53 fn partial_cmp(&self, other: &Package) -> Option<Ordering> {
54 Some(self.cmp(other))
55 }
56}
57
58#[derive(Serialize)]
60struct SerializedPackage<'a> {
61 name: &'a str,
62 version: &'a Version,
63 id: PackageId,
64 license: Option<&'a str>,
65 license_file: Option<&'a str>,
66 description: Option<&'a str>,
67 source: SourceId,
68 dependencies: &'a [Dependency],
69 targets: Vec<&'a Target>,
70 features: &'a FeatureMap,
71 manifest_path: &'a Path,
72 metadata: Option<&'a toml::Value>,
73 publish: Option<&'a Vec<String>>,
74 authors: &'a [String],
75 categories: &'a [String],
76 keywords: &'a [String],
77 readme: Option<&'a str>,
78 repository: Option<&'a str>,
79 edition: &'a str,
80 links: Option<&'a str>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 metabuild: Option<&'a Vec<String>>,
83}
84
85impl ser::Serialize for Package {
86 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
87 where
88 S: ser::Serializer,
89 {
90 let summary = self.manifest.summary();
91 let package_id = summary.package_id();
92 let manmeta = self.manifest.metadata();
93 let license = manmeta.license.as_deref();
94 let license_file = manmeta.license_file.as_deref();
95 let description = manmeta.description.as_deref();
96 let authors = manmeta.authors.as_ref();
97 let categories = manmeta.categories.as_ref();
98 let keywords = manmeta.keywords.as_ref();
99 let readme = manmeta.readme.as_deref();
100 let repository = manmeta.repository.as_deref();
101 let targets: Vec<&Target> = self
105 .manifest
106 .targets()
107 .iter()
108 .filter(|t| t.src_path().is_path())
109 .collect();
110
111 SerializedPackage {
112 name: &*package_id.name(),
113 version: package_id.version(),
114 id: package_id,
115 license,
116 license_file,
117 description,
118 source: summary.source_id(),
119 dependencies: summary.dependencies(),
120 targets,
121 features: summary.features(),
122 manifest_path: &self.manifest_path,
123 metadata: self.manifest.custom_metadata(),
124 authors,
125 categories,
126 keywords,
127 readme,
128 repository,
129 edition: &self.manifest.edition().to_string(),
130 links: self.manifest.links(),
131 metabuild: self.manifest.metabuild(),
132 publish: self.publish().as_ref(),
133 }
134 .serialize(s)
135 }
136}
137
138impl Package {
139 pub fn new(manifest: Manifest, manifest_path: &Path) -> Package {
141 Package {
142 manifest,
143 manifest_path: manifest_path.to_path_buf(),
144 }
145 }
146
147 pub fn dependencies(&self) -> &[Dependency] {
149 self.manifest.dependencies()
150 }
151 pub fn manifest(&self) -> &Manifest {
153 &self.manifest
154 }
155 pub fn manifest_mut(&mut self) -> &mut Manifest {
157 &mut self.manifest
158 }
159 pub fn manifest_path(&self) -> &Path {
161 &self.manifest_path
162 }
163 pub fn name(&self) -> InternedString {
165 self.package_id().name()
166 }
167 pub fn package_id(&self) -> PackageId {
169 self.manifest.package_id()
170 }
171 pub fn root(&self) -> &Path {
173 self.manifest_path.parent().unwrap()
174 }
175 pub fn summary(&self) -> &Summary {
177 self.manifest.summary()
178 }
179 pub fn targets(&self) -> &[Target] {
181 self.manifest.targets()
182 }
183 pub fn version(&self) -> &Version {
185 self.package_id().version()
186 }
187 pub fn authors(&self) -> &Vec<String> {
189 &self.manifest.metadata().authors
190 }
191 pub fn publish(&self) -> &Option<Vec<String>> {
193 self.manifest.publish()
194 }
195 pub fn proc_macro(&self) -> bool {
197 self.targets().iter().any(|target| target.proc_macro())
198 }
199
200 pub fn has_custom_build(&self) -> bool {
202 self.targets().iter().any(|t| t.is_custom_build())
203 }
204
205 pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Package {
206 Package {
207 manifest: self.manifest.map_source(to_replace, replace_with),
208 manifest_path: self.manifest_path,
209 }
210 }
211
212 pub fn to_registry_toml(&self, config: &Config) -> CargoResult<String> {
213 let manifest = self
214 .manifest()
215 .original()
216 .prepare_for_publish(config, self.root())?;
217 let toml = toml::to_string(&manifest)?;
218 Ok(format!(
219 "# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO\n\
220 #\n\
221 # When uploading crates to the registry Cargo will automatically\n\
222 # \"normalize\" Cargo.toml files for maximal compatibility\n\
223 # with all versions of Cargo and also rewrite `path` dependencies\n\
224 # to registry (e.g., crates.io) dependencies\n\
225 #\n\
226 # If you believe there's an error in this file please file an\n\
227 # issue against the rust-lang/cargo repository. If you're\n\
228 # editing this file be aware that the upstream Cargo.toml\n\
229 # will likely look very different (and much more reasonable)\n\
230 \n\
231 {}\
232 ",
233 toml
234 ))
235 }
236
237 pub fn include_lockfile(&self) -> bool {
239 self.targets().iter().any(|t| t.is_example() || t.is_bin())
240 }
241}
242
243impl fmt::Display for Package {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 write!(f, "{}", self.summary().package_id())
246 }
247}
248
249impl fmt::Debug for Package {
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 f.debug_struct("Package")
252 .field("id", &self.summary().package_id())
253 .field("..", &"..")
254 .finish()
255 }
256}
257
258impl PartialEq for Package {
259 fn eq(&self, other: &Package) -> bool {
260 self.package_id() == other.package_id()
261 }
262}
263
264impl Eq for Package {}
265
266impl hash::Hash for Package {
267 fn hash<H: hash::Hasher>(&self, into: &mut H) {
268 self.package_id().hash(into)
269 }
270}
271
272pub struct PackageSet<'cfg> {
277 packages: HashMap<PackageId, LazyCell<Package>>,
278 sources: RefCell<SourceMap<'cfg>>,
279 config: &'cfg Config,
280 multi: Multi,
281 downloading: Cell<bool>,
283 multiplexing: bool,
285}
286
287pub struct Downloads<'a, 'cfg> {
289 set: &'a PackageSet<'cfg>,
290 pending: HashMap<usize, (Download<'cfg>, EasyHandle)>,
294 pending_ids: HashSet<PackageId>,
297 results: Vec<(usize, Result<(), curl::Error>)>,
302 next: usize,
304 progress: RefCell<Option<Progress<'cfg>>>,
306 downloads_finished: usize,
308 downloaded_bytes: u64,
310 largest: (u64, String),
312 start: Instant,
314 success: bool,
316
317 timeout: ops::HttpTimeout,
324 updated_at: Cell<Instant>,
326 next_speed_check: Cell<Instant>,
331 next_speed_check_bytes_threshold: Cell<u64>,
338 _lock: PackageCacheLock<'cfg>,
341}
342
343struct Download<'cfg> {
344 token: usize,
347
348 id: PackageId,
350
351 data: RefCell<Vec<u8>>,
353
354 url: String,
357
358 descriptor: String,
360
361 total: Cell<u64>,
363 current: Cell<u64>,
364
365 start: Instant,
367 timed_out: Cell<Option<String>>,
368
369 retry: Retry<'cfg>,
371}
372
373impl<'cfg> PackageSet<'cfg> {
374 pub fn new(
375 package_ids: &[PackageId],
376 sources: SourceMap<'cfg>,
377 config: &'cfg Config,
378 ) -> CargoResult<PackageSet<'cfg>> {
379 let mut multi = Multi::new();
389 let multiplexing = config.http_config()?.multiplexing.unwrap_or(true);
390 multi
391 .pipelining(false, multiplexing)
392 .chain_err(|| "failed to enable multiplexing/pipelining in curl")?;
393
394 multi.set_max_host_connections(2)?;
396
397 Ok(PackageSet {
398 packages: package_ids
399 .iter()
400 .map(|&id| (id, LazyCell::new()))
401 .collect(),
402 sources: RefCell::new(sources),
403 config,
404 multi,
405 downloading: Cell::new(false),
406 multiplexing,
407 })
408 }
409
410 pub fn package_ids<'a>(&'a self) -> impl Iterator<Item = PackageId> + 'a {
411 self.packages.keys().cloned()
412 }
413
414 pub fn enable_download<'a>(&'a self) -> CargoResult<Downloads<'a, 'cfg>> {
415 assert!(!self.downloading.replace(true));
416 let timeout = ops::HttpTimeout::new(self.config)?;
417 Ok(Downloads {
418 start: Instant::now(),
419 set: self,
420 next: 0,
421 pending: HashMap::new(),
422 pending_ids: HashSet::new(),
423 results: Vec::new(),
424 progress: RefCell::new(Some(Progress::with_style(
425 "Downloading",
426 ProgressStyle::Ratio,
427 self.config,
428 ))),
429 downloads_finished: 0,
430 downloaded_bytes: 0,
431 largest: (0, String::new()),
432 success: false,
433 updated_at: Cell::new(Instant::now()),
434 timeout,
435 next_speed_check: Cell::new(Instant::now()),
436 next_speed_check_bytes_threshold: Cell::new(0),
437 _lock: self.config.acquire_package_cache_lock()?,
438 })
439 }
440
441 pub fn get_one(&self, id: PackageId) -> CargoResult<&Package> {
442 if let Some(pkg) = self.packages.get(&id).and_then(|slot| slot.borrow()) {
443 return Ok(pkg);
444 }
445 Ok(self.get_many(Some(id))?.remove(0))
446 }
447
448 pub fn get_many(&self, ids: impl IntoIterator<Item = PackageId>) -> CargoResult<Vec<&Package>> {
449 let mut pkgs = Vec::new();
450 let mut downloads = self.enable_download()?;
451 for id in ids {
452 pkgs.extend(downloads.start(id)?);
453 }
454 while downloads.remaining() > 0 {
455 pkgs.push(downloads.wait()?);
456 }
457 downloads.success = true;
458 Ok(pkgs)
459 }
460
461 pub fn download_accessible(
463 &self,
464 resolve: &Resolve,
465 root_ids: &[PackageId],
466 has_dev_units: HasDevUnits,
467 requested_kind: CompileKind,
468 target_data: &RustcTargetData,
469 ) -> CargoResult<()> {
470 fn collect_used_deps(
471 used: &mut BTreeSet<PackageId>,
472 resolve: &Resolve,
473 pkg_id: PackageId,
474 has_dev_units: HasDevUnits,
475 requested_kind: CompileKind,
476 target_data: &RustcTargetData,
477 ) -> CargoResult<()> {
478 if !used.insert(pkg_id) {
479 return Ok(());
480 }
481 let filtered_deps = resolve.deps(pkg_id).filter(|&(_id, deps)| {
482 deps.iter().any(|dep| {
483 if dep.kind() == DepKind::Development && has_dev_units == HasDevUnits::No {
484 return false;
485 }
486 if !target_data.dep_platform_activated(dep, requested_kind)
491 && !target_data.dep_platform_activated(dep, CompileKind::Host)
492 {
493 return false;
494 }
495 true
496 })
497 });
498 for (dep_id, _deps) in filtered_deps {
499 collect_used_deps(
500 used,
501 resolve,
502 dep_id,
503 has_dev_units,
504 requested_kind,
505 target_data,
506 )?;
507 }
508 Ok(())
509 }
510
511 let mut to_download = BTreeSet::new();
515
516 for id in root_ids {
517 collect_used_deps(
518 &mut to_download,
519 resolve,
520 *id,
521 has_dev_units,
522 requested_kind,
523 target_data,
524 )?;
525 }
526 self.get_many(to_download.into_iter())?;
527 Ok(())
528 }
529
530 pub fn sources(&self) -> Ref<'_, SourceMap<'cfg>> {
531 self.sources.borrow()
532 }
533
534 pub fn sources_mut(&self) -> RefMut<'_, SourceMap<'cfg>> {
535 self.sources.borrow_mut()
536 }
537
538 pub fn add_set(&mut self, set: PackageSet<'cfg>) {
540 assert!(!self.downloading.get());
541 assert!(!set.downloading.get());
542 for (pkg_id, p_cell) in set.packages {
543 self.packages.entry(pkg_id).or_insert(p_cell);
544 }
545 let mut sources = self.sources.borrow_mut();
546 let other_sources = set.sources.into_inner();
547 sources.add_source_map(other_sources);
548 }
549
550 pub fn lookup_mut(&mut self, id: PackageId) -> Option<&mut Package> {
554 self.packages
555 .get_mut(&id)
556 .and_then(|cell| cell.borrow_mut())
557 }
558}
559
560macro_rules! try_old_curl {
563 ($e:expr, $msg:expr) => {
564 let result = $e;
565 if cfg!(target_os = "macos") {
566 if let Err(e) = result {
567 warn!("ignoring libcurl {} error: {}", $msg, e);
568 }
569 } else {
570 result.with_context(|| {
571 anyhow::format_err!("failed to enable {}, is curl not built right?", $msg)
572 })?;
573 }
574 };
575}
576
577impl<'a, 'cfg> Downloads<'a, 'cfg> {
578 pub fn start(&mut self, id: PackageId) -> CargoResult<Option<&'a Package>> {
584 Ok(self
585 .start_inner(id)
586 .chain_err(|| format!("failed to download `{}`", id))?)
587 }
588
589 fn start_inner(&mut self, id: PackageId) -> CargoResult<Option<&'a Package>> {
590 let slot = self
593 .set
594 .packages
595 .get(&id)
596 .ok_or_else(|| internal(format!("couldn't find `{}` in package set", id)))?;
597 if let Some(pkg) = slot.borrow() {
598 return Ok(Some(pkg));
599 }
600
601 let mut sources = self.set.sources.borrow_mut();
605 let source = sources
606 .get_mut(id.source_id())
607 .ok_or_else(|| internal(format!("couldn't find source for `{}`", id)))?;
608 let pkg = source
609 .download(id)
610 .chain_err(|| anyhow::format_err!("unable to get packages from source"))?;
611 let (url, descriptor) = match pkg {
612 MaybePackage::Ready(pkg) => {
613 debug!("{} doesn't need a download", id);
614 assert!(slot.fill(pkg).is_ok());
615 return Ok(Some(slot.borrow().unwrap()));
616 }
617 MaybePackage::Download { url, descriptor } => (url, descriptor),
618 };
619
620 let token = self.next;
625 self.next += 1;
626 debug!("downloading {} as {}", id, token);
627 assert!(self.pending_ids.insert(id));
628
629 let (mut handle, _timeout) = ops::http_handle_and_timeout(self.set.config)?;
630 handle.get(true)?;
631 handle.url(&url)?;
632 handle.follow_location(true)?; if self.set.multiplexing {
647 try_old_curl!(handle.http_version(HttpVersion::V2), "HTTP2");
648 } else {
649 handle.http_version(HttpVersion::V11)?;
650 }
651
652 try_old_curl!(handle.pipewait(true), "pipewait");
660
661 handle.write_function(move |buf| {
662 debug!("{} - {} bytes of data", token, buf.len());
663 tls::with(|downloads| {
664 if let Some(downloads) = downloads {
665 downloads.pending[&token]
666 .0
667 .data
668 .borrow_mut()
669 .extend_from_slice(buf);
670 }
671 });
672 Ok(buf.len())
673 })?;
674
675 handle.progress(true)?;
676 handle.progress_function(move |dl_total, dl_cur, _, _| {
677 tls::with(|downloads| match downloads {
678 Some(d) => d.progress(token, dl_total as u64, dl_cur as u64),
679 None => false,
680 })
681 })?;
682
683 if self.downloads_finished == 0
687 && self.pending.is_empty()
688 && !self.progress.borrow().as_ref().unwrap().is_enabled()
689 {
690 self.set
691 .config
692 .shell()
693 .status("Downloading", "crates ...")?;
694 }
695
696 let dl = Download {
697 token,
698 data: RefCell::new(Vec::new()),
699 id,
700 url,
701 descriptor,
702 total: Cell::new(0),
703 current: Cell::new(0),
704 start: Instant::now(),
705 timed_out: Cell::new(None),
706 retry: Retry::new(self.set.config)?,
707 };
708 self.enqueue(dl, handle)?;
709 self.tick(WhyTick::DownloadStarted)?;
710
711 Ok(None)
712 }
713
714 pub fn remaining(&self) -> usize {
716 self.pending.len()
717 }
718
719 pub fn wait(&mut self) -> CargoResult<&'a Package> {
728 let (dl, data) = loop {
729 assert_eq!(self.pending.len(), self.pending_ids.len());
730 let (token, result) = self.wait_for_curl()?;
731 debug!("{} finished with {:?}", token, result);
732
733 let (mut dl, handle) = self
734 .pending
735 .remove(&token)
736 .expect("got a token for a non-in-progress transfer");
737 let data = mem::replace(&mut *dl.data.borrow_mut(), Vec::new());
738 let mut handle = self.set.multi.remove(handle)?;
739 self.pending_ids.remove(&dl.id);
740
741 let ret = {
745 let timed_out = &dl.timed_out;
746 let url = &dl.url;
747 dl.retry
748 .r#try(|| {
749 if let Err(e) = result {
750 if !e.is_aborted_by_callback() {
758 return Err(e.into());
759 }
760
761 return Err(match timed_out.replace(None) {
762 Some(msg) => {
763 let code = curl_sys::CURLE_OPERATION_TIMEDOUT;
764 let mut err = curl::Error::new(code);
765 err.set_extra(msg);
766 err
767 }
768 None => e,
769 }
770 .into());
771 }
772
773 let code = handle.response_code()?;
774 if code != 200 && code != 0 {
775 let url = handle.effective_url()?.unwrap_or(url);
776 return Err(HttpNot200 {
777 code,
778 url: url.to_string(),
779 }
780 .into());
781 }
782 Ok(())
783 })
784 .chain_err(|| format!("failed to download from `{}`", dl.url))?
785 };
786 match ret {
787 Some(()) => break (dl, data),
788 None => {
789 self.pending_ids.insert(dl.id);
790 self.enqueue(dl, handle)?
791 }
792 }
793 };
794
795 self.progress.borrow_mut().as_mut().unwrap().clear();
799 self.set
800 .config
801 .shell()
802 .status("Downloaded", &dl.descriptor)?;
803
804 self.downloads_finished += 1;
805 self.downloaded_bytes += dl.total.get();
806 if dl.total.get() > self.largest.0 {
807 self.largest = (dl.total.get(), dl.id.name().to_string());
808 }
809
810 if dl.total.get() < ByteSize::kb(400).0 {
816 self.tick(WhyTick::DownloadFinished)?;
817 } else {
818 self.tick(WhyTick::Extracting(&dl.id.name()))?;
819 }
820
821 let mut sources = self.set.sources.borrow_mut();
824 let source = sources
825 .get_mut(dl.id.source_id())
826 .ok_or_else(|| internal(format!("couldn't find source for `{}`", dl.id)))?;
827 let start = Instant::now();
828 let pkg = source.finish_download(dl.id, data)?;
829
830 let finish_dur = start.elapsed();
835 self.updated_at.set(self.updated_at.get() + finish_dur);
836 self.next_speed_check
837 .set(self.next_speed_check.get() + finish_dur);
838
839 let slot = &self.set.packages[&dl.id];
840 assert!(slot.fill(pkg).is_ok());
841 Ok(slot.borrow().unwrap())
842 }
843
844 fn enqueue(&mut self, dl: Download<'cfg>, handle: Easy) -> CargoResult<()> {
845 let mut handle = self.set.multi.add(handle)?;
846 let now = Instant::now();
847 handle.set_token(dl.token)?;
848 self.updated_at.set(now);
849 self.next_speed_check.set(now + self.timeout.dur);
850 self.next_speed_check_bytes_threshold
851 .set(u64::from(self.timeout.low_speed_limit));
852 dl.timed_out.set(None);
853 dl.current.set(0);
854 dl.total.set(0);
855 self.pending.insert(dl.token, (dl, handle));
856 Ok(())
857 }
858
859 fn wait_for_curl(&mut self) -> CargoResult<(usize, Result<(), curl::Error>)> {
862 loop {
878 let n = tls::set(self, || {
879 self.set
880 .multi
881 .perform()
882 .chain_err(|| "failed to perform http requests")
883 })?;
884 debug!("handles remaining: {}", n);
885 let results = &mut self.results;
886 let pending = &self.pending;
887 self.set.multi.messages(|msg| {
888 let token = msg.token().expect("failed to read token");
889 let handle = &pending[&token].1;
890 if let Some(result) = msg.result_for(handle) {
891 results.push((token, result));
892 } else {
893 debug!("message without a result (?)");
894 }
895 });
896
897 if let Some(pair) = results.pop() {
898 break Ok(pair);
899 }
900 assert!(!self.pending.is_empty());
901 let timeout = self
902 .set
903 .multi
904 .get_timeout()?
905 .unwrap_or_else(|| Duration::new(5, 0));
906 self.set
907 .multi
908 .wait(&mut [], timeout)
909 .chain_err(|| "failed to wait on curl `Multi`")?;
910 }
911 }
912
913 fn progress(&self, token: usize, total: u64, cur: u64) -> bool {
914 let dl = &self.pending[&token].0;
915 dl.total.set(total);
916 let now = Instant::now();
917 if cur != dl.current.get() {
918 let delta = cur - dl.current.get();
919 let threshold = self.next_speed_check_bytes_threshold.get();
920
921 dl.current.set(cur);
922 self.updated_at.set(now);
923
924 if delta >= threshold {
925 self.next_speed_check.set(now + self.timeout.dur);
926 self.next_speed_check_bytes_threshold
927 .set(u64::from(self.timeout.low_speed_limit));
928 } else {
929 self.next_speed_check_bytes_threshold.set(threshold - delta);
930 }
931 }
932 if self.tick(WhyTick::DownloadUpdate).is_err() {
933 return false;
934 }
935
936 if now - self.updated_at.get() > self.timeout.dur {
938 self.updated_at.set(now);
939 let msg = format!(
940 "failed to download any data for `{}` within {}s",
941 dl.id,
942 self.timeout.dur.as_secs()
943 );
944 dl.timed_out.set(Some(msg));
945 return false;
946 }
947
948 if now >= self.next_speed_check.get() {
953 self.next_speed_check.set(now + self.timeout.dur);
954 assert!(self.next_speed_check_bytes_threshold.get() > 0);
955 let msg = format!(
956 "download of `{}` failed to transfer more \
957 than {} bytes in {}s",
958 dl.id,
959 self.timeout.low_speed_limit,
960 self.timeout.dur.as_secs()
961 );
962 dl.timed_out.set(Some(msg));
963 return false;
964 }
965
966 true
967 }
968
969 fn tick(&self, why: WhyTick<'_>) -> CargoResult<()> {
970 let mut progress = self.progress.borrow_mut();
971 let progress = progress.as_mut().unwrap();
972
973 if let WhyTick::DownloadUpdate = why {
974 if !progress.update_allowed() {
975 return Ok(());
976 }
977 }
978 let pending = self.pending.len();
979 let mut msg = if pending == 1 {
980 format!("{} crate", pending)
981 } else {
982 format!("{} crates", pending)
983 };
984 match why {
985 WhyTick::Extracting(krate) => {
986 msg.push_str(&format!(", extracting {} ...", krate));
987 }
988 _ => {
989 let mut dur = Duration::new(0, 0);
990 let mut remaining = 0;
991 for (dl, _) in self.pending.values() {
992 dur += dl.start.elapsed();
993 if dl.total.get() >= dl.current.get() {
997 remaining += dl.total.get() - dl.current.get();
998 }
999 }
1000 if remaining > 0 && dur > Duration::from_millis(500) {
1001 msg.push_str(&format!(", remaining bytes: {}", ByteSize(remaining)));
1002 }
1003 }
1004 }
1005 progress.print_now(&msg)
1006 }
1007}
1008
1009#[derive(Copy, Clone)]
1010enum WhyTick<'a> {
1011 DownloadStarted,
1012 DownloadUpdate,
1013 DownloadFinished,
1014 Extracting(&'a str),
1015}
1016
1017impl<'a, 'cfg> Drop for Downloads<'a, 'cfg> {
1018 fn drop(&mut self) {
1019 self.set.downloading.set(false);
1020 let progress = self.progress.get_mut().take().unwrap();
1021 if !progress.is_enabled() {
1024 return;
1025 }
1026 if self.downloads_finished == 0 {
1028 return;
1029 }
1030 if !self.success {
1032 return;
1033 }
1034 let crate_string = if self.downloads_finished == 1 {
1036 "crate"
1037 } else {
1038 "crates"
1039 };
1040 let mut status = format!(
1041 "{} {} ({}) in {}",
1042 self.downloads_finished,
1043 crate_string,
1044 ByteSize(self.downloaded_bytes),
1045 util::elapsed(self.start.elapsed())
1046 );
1047 if self.largest.0 > ByteSize::mb(1).0 && self.downloads_finished > 1 {
1051 status.push_str(&format!(
1052 " (largest was `{}` at {})",
1053 self.largest.1,
1054 ByteSize(self.largest.0),
1055 ));
1056 }
1057 drop(progress);
1059 drop(self.set.config.shell().status("Downloaded", status));
1060 }
1061}
1062
1063mod tls {
1064 use std::cell::Cell;
1065
1066 use super::Downloads;
1067
1068 thread_local!(static PTR: Cell<usize> = Cell::new(0));
1069
1070 pub(crate) fn with<R>(f: impl FnOnce(Option<&Downloads<'_, '_>>) -> R) -> R {
1071 let ptr = PTR.with(|p| p.get());
1072 if ptr == 0 {
1073 f(None)
1074 } else {
1075 unsafe { f(Some(&*(ptr as *const Downloads<'_, '_>))) }
1076 }
1077 }
1078
1079 pub(crate) fn set<R>(dl: &Downloads<'_, '_>, f: impl FnOnce() -> R) -> R {
1080 struct Reset<'a, T: Copy>(&'a Cell<T>, T);
1081
1082 impl<'a, T: Copy> Drop for Reset<'a, T> {
1083 fn drop(&mut self) {
1084 self.0.set(self.1);
1085 }
1086 }
1087
1088 PTR.with(|p| {
1089 let _reset = Reset(p, p.get());
1090 p.set(dl as *const Downloads<'_, '_> as usize);
1091 f()
1092 })
1093 }
1094}