1use std::borrow::Cow;
43use std::ffi::OsStr;
44use std::fmt::Display;
45use std::path;
46use std::path::{Path, PathBuf};
47use std::str::FromStr;
48
49use memchr::memchr3;
50use url::Url;
51
52use uv_distribution_filename::{
53 DistExtension, SourceDistExtension, SourceDistFilename, WheelFilename,
54};
55use uv_fs::normalize_absolute_path;
56use uv_git_types::GitUrl;
57use uv_normalize::PackageName;
58use uv_pep440::Version;
59use uv_pep508::{Pep508Url, VerbatimUrl};
60use uv_pypi_types::{
61 ParsedArchiveUrl, ParsedDirectoryUrl, ParsedGitDirectoryUrl, ParsedGitPathUrl, ParsedPathUrl,
62 ParsedUrl, VerbatimParsedUrl,
63};
64use uv_redacted::DisplaySafeUrl;
65
66pub use crate::annotation::*;
67pub use crate::any::*;
68pub use crate::build_info::*;
69pub use crate::build_requires::*;
70pub use crate::buildable::*;
71pub use crate::cached::*;
72pub use crate::config_settings::*;
73pub use crate::dependency_metadata::*;
74pub use crate::diagnostic::*;
75pub use crate::dist_error::*;
76pub use crate::error::*;
77pub use crate::exclude_newer::*;
78pub use crate::file::*;
79pub use crate::hash::*;
80pub use crate::id::*;
81pub use crate::index::*;
82pub use crate::index_name::*;
83pub use crate::index_url::*;
84pub use crate::installed::*;
85pub use crate::known_platform::*;
86pub use crate::origin::*;
87pub use crate::pip_index::*;
88pub use crate::prioritized_distribution::*;
89pub use crate::requested::*;
90pub use crate::requirement::*;
91pub use crate::requires_python::*;
92pub use crate::resolution::*;
93pub use crate::resolved::*;
94pub use crate::specified_requirement::*;
95pub use crate::status_code_strategy::*;
96pub use crate::traits::*;
97
98mod annotation;
99mod any;
100mod build_info;
101mod build_requires;
102mod buildable;
103mod cached;
104mod config_settings;
105mod dependency_metadata;
106mod diagnostic;
107mod dist_error;
108mod error;
109mod exclude_newer;
110mod file;
111mod hash;
112mod id;
113mod index;
114mod index_name;
115mod index_url;
116mod installed;
117mod installed_modules;
118mod known_platform;
119mod origin;
120mod pip_index;
121mod prioritized_distribution;
122mod requested;
123mod requirement;
124mod requires_python;
125mod resolution;
126mod resolved;
127mod specified_requirement;
128mod status_code_strategy;
129mod traits;
130
131#[derive(Debug, Clone)]
132pub enum VersionOrUrlRef<'a, T: Pep508Url = VerbatimUrl> {
133 Version(&'a Version),
135 Url(&'a T),
137}
138
139impl Verbatim for VersionOrUrlRef<'_> {
140 fn verbatim(&self) -> Cow<'_, str> {
141 match self {
142 Self::Version(version) => Cow::Owned(format!("=={version}")),
143 Self::Url(url) => Cow::Owned(format!(" @ {}", url.verbatim())),
144 }
145 }
146}
147
148impl std::fmt::Display for VersionOrUrlRef<'_> {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 match self {
151 Self::Version(version) => write!(f, "=={version}"),
152 Self::Url(url) => write!(f, " @ {url}"),
153 }
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
158pub enum InstalledVersion<'a> {
159 Version(&'a Version),
161 Url(&'a DisplaySafeUrl, &'a Version),
164}
165
166impl<'a> InstalledVersion<'a> {
167 pub fn version(&self) -> &'a Version {
169 match self {
170 Self::Version(version) => version,
171 Self::Url(_, version) => version,
172 }
173 }
174}
175
176impl std::fmt::Display for InstalledVersion<'_> {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 match self {
179 Self::Version(version) => write!(f, "=={version}"),
180 Self::Url(url, version) => write!(f, "=={version} (from {url})"),
181 }
182 }
183}
184
185#[derive(Debug, Clone, Hash, PartialEq, Eq)]
189pub enum Dist {
190 Built(BuiltDist),
191 Source(SourceDist),
192}
193
194#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
196pub enum DistRef<'a> {
197 Built(&'a BuiltDist),
198 Source(&'a SourceDist),
199}
200
201impl Display for DistRef<'_> {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 match self {
204 Self::Built(built_dist) => Display::fmt(&built_dist, f),
205 Self::Source(source_dist) => Display::fmt(&source_dist, f),
206 }
207 }
208}
209
210#[derive(Debug, Clone, Hash, PartialEq, Eq)]
212pub enum BuiltDist {
213 Registry(RegistryBuiltDist),
214 DirectUrl(DirectUrlBuiltDist),
215 Path(PathBuiltDist),
216 GitPath(GitPathBuiltDist),
217}
218
219#[derive(Debug, Clone, Hash, PartialEq, Eq)]
222pub enum SourceDist {
223 Registry(RegistrySourceDist),
224 DirectUrl(DirectUrlSourceDist),
225 GitDirectory(GitDirectorySourceDist),
226 GitPath(GitPathSourceDist),
227 Path(PathSourceDist),
228 Directory(DirectorySourceDist),
229}
230
231#[derive(Debug, Clone, Hash, PartialEq, Eq)]
233pub struct RegistryBuiltWheel {
234 pub filename: WheelFilename,
235 pub file: Box<File>,
236 pub index: IndexUrl,
237 pub size_is_authoritative: bool,
239}
240
241#[derive(Debug, Clone, Hash, PartialEq, Eq)]
243pub struct RegistryBuiltDist {
244 pub wheels: Vec<RegistryBuiltWheel>,
247 pub best_wheel_index: usize,
252 pub sdist: Option<RegistrySourceDist>,
260 }
270
271#[derive(Debug, Clone, Hash, PartialEq, Eq)]
273pub struct DirectUrlBuiltDist {
274 pub filename: WheelFilename,
277 pub location: Box<DisplaySafeUrl>,
279 pub url: VerbatimUrl,
281 pub size: Option<u64>,
283}
284
285#[derive(Debug, Clone, Hash, PartialEq, Eq)]
287pub struct PathBuiltDist {
288 pub filename: WheelFilename,
289 pub install_path: Box<Path>,
291 pub url: VerbatimUrl,
293}
294
295#[derive(Debug, Clone, Hash, PartialEq, Eq)]
297pub struct GitPathBuiltDist {
298 pub filename: WheelFilename,
299 pub git: Box<GitUrl>,
301 pub install_path: PathBuf,
303 pub url: VerbatimUrl,
305}
306
307#[derive(Debug, Clone, Hash, PartialEq, Eq)]
309pub struct RegistrySourceDist {
310 pub name: PackageName,
311 pub version: Version,
312 pub file: Box<File>,
313 pub ext: SourceDistExtension,
315 pub index: IndexUrl,
316 pub wheels: Vec<RegistryBuiltWheel>,
324 pub size_is_authoritative: bool,
326}
327
328#[derive(Debug, Clone, Hash, PartialEq, Eq)]
330pub struct DirectUrlSourceDist {
331 pub name: PackageName,
334 pub location: Box<DisplaySafeUrl>,
336 pub subdirectory: Option<Box<Path>>,
338 pub ext: SourceDistExtension,
340 pub url: VerbatimUrl,
342 pub size: Option<u64>,
344}
345
346#[derive(Debug, Clone, Hash, PartialEq, Eq)]
348pub struct GitDirectorySourceDist {
349 pub name: PackageName,
350 pub git: Box<GitUrl>,
352 pub subdirectory: Option<Box<Path>>,
354 pub url: VerbatimUrl,
356}
357
358#[derive(Debug, Clone, Hash, PartialEq, Eq)]
361pub struct GitPathSourceDist {
362 pub name: PackageName,
363 pub git: Box<GitUrl>,
365 pub install_path: PathBuf,
367 pub ext: SourceDistExtension,
369 pub url: VerbatimUrl,
371}
372
373#[derive(Debug, Clone, Hash, PartialEq, Eq)]
375pub struct PathSourceDist {
376 pub name: PackageName,
377 pub version: Option<Version>,
378 pub install_path: Box<Path>,
380 pub ext: SourceDistExtension,
382 pub url: VerbatimUrl,
384}
385
386#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
388pub enum FirstParty {
389 Yes,
390 No,
391}
392
393#[derive(Debug, Clone, Hash, PartialEq, Eq)]
395pub struct DirectorySourceDist {
396 pub name: PackageName,
397 pub install_path: Box<Path>,
399 pub editable: Option<bool>,
401 pub r#virtual: Option<bool>,
403 pub first_party: FirstParty,
405 pub url: VerbatimUrl,
407}
408
409impl Dist {
410 pub fn from_http_url(
413 name: PackageName,
414 url: VerbatimUrl,
415 location: DisplaySafeUrl,
416 subdirectory: Option<Box<Path>>,
417 ext: DistExtension,
418 ) -> Result<Self, Error> {
419 match ext {
420 DistExtension::Wheel => {
421 let filename = WheelFilename::from_str(&url.filename()?)?;
423 if filename.name != name {
424 return Err(Error::PackageNameMismatch(
425 name,
426 filename.name,
427 url.verbatim().to_string(),
428 ));
429 }
430
431 Ok(Self::Built(BuiltDist::DirectUrl(DirectUrlBuiltDist {
432 filename,
433 location: Box::new(location),
434 url,
435 size: None,
436 })))
437 }
438 DistExtension::Source(ext) => {
439 if !ext.is_pep625_compliant() {
440 return Err(Error::NotPep625Filename(url.verbatim().to_string()));
441 }
442 Ok(Self::Source(SourceDist::DirectUrl(DirectUrlSourceDist {
443 name,
444 location: Box::new(location),
445 subdirectory,
446 ext,
447 url,
448 size: None,
449 })))
450 }
451 }
452 }
453
454 pub fn from_file_url(
456 name: PackageName,
457 url: VerbatimUrl,
458 install_path: &Path,
459 ext: DistExtension,
460 ) -> Result<Self, Error> {
461 let install_path = path::absolute(install_path)?;
463
464 let install_path = normalize_absolute_path(&install_path)?;
466
467 if !install_path.exists() {
469 return Err(Error::NotFound(url.to_url()));
470 }
471
472 match ext {
474 DistExtension::Wheel => {
475 let filename = install_path
477 .file_name()
478 .and_then(OsStr::to_str)
479 .ok_or_else(|| Error::MissingWheelFilename(install_path.clone()))?;
480 let filename = WheelFilename::from_str(filename)?;
481 if filename.name != name {
482 return Err(Error::PackageNameMismatch(
483 name,
484 filename.name,
485 url.verbatim().to_string(),
486 ));
487 }
488 Ok(Self::Built(BuiltDist::Path(PathBuiltDist {
489 filename,
490 install_path: install_path.into_boxed_path(),
491 url,
492 })))
493 }
494 DistExtension::Source(ext) => {
495 if !ext.is_pep625_compliant() {
496 return Err(Error::NotPep625Filename(url.verbatim().to_string()));
497 }
498
499 let version = url
501 .filename()
502 .ok()
503 .and_then(|filename| {
504 SourceDistFilename::parse(filename.as_ref(), ext, &name).ok()
505 })
506 .map(|filename| filename.version);
507
508 Ok(Self::Source(SourceDist::Path(PathSourceDist {
509 name,
510 version,
511 install_path: install_path.into_boxed_path(),
512 ext,
513 url,
514 })))
515 }
516 }
517 }
518
519 pub fn from_directory_url(
521 name: PackageName,
522 url: VerbatimUrl,
523 install_path: &Path,
524 editable: Option<bool>,
525 r#virtual: Option<bool>,
526 ) -> Result<Self, Error> {
527 let install_path = path::absolute(install_path)?;
529
530 let install_path = normalize_absolute_path(&install_path)?;
532
533 if !install_path.exists() {
535 return Err(Error::NotFound(url.to_url()));
536 }
537
538 Ok(Self::Source(SourceDist::Directory(DirectorySourceDist {
540 name,
541 install_path: install_path.into_boxed_path(),
542 editable,
543 r#virtual,
544 first_party: FirstParty::No,
545 url,
546 })))
547 }
548
549 pub fn from_git_directory_url(
551 name: PackageName,
552 url: VerbatimUrl,
553 git: GitUrl,
554 subdirectory: Option<Box<Path>>,
555 ) -> Result<Self, Error> {
556 Ok(Self::Source(SourceDist::GitDirectory(
557 GitDirectorySourceDist {
558 name,
559 git: Box::new(git),
560 subdirectory,
561 url,
562 },
563 )))
564 }
565
566 pub fn from_git_path_url(
568 name: PackageName,
569 url: VerbatimUrl,
570 git: GitUrl,
571 install_path: PathBuf,
572 ext: DistExtension,
573 ) -> Result<Self, Error> {
574 match ext {
575 DistExtension::Wheel => {
576 let filename = install_path
578 .file_name()
579 .and_then(OsStr::to_str)
580 .ok_or_else(|| Error::MissingWheelFilename(install_path.clone()))?;
581 let filename = WheelFilename::from_str(filename)?;
582 if filename.name != name {
583 return Err(Error::PackageNameMismatch(
584 name,
585 filename.name,
586 url.verbatim().to_string(),
587 ));
588 }
589
590 Ok(Self::Built(BuiltDist::GitPath(GitPathBuiltDist {
591 filename,
592 git: Box::new(git),
593 install_path,
594 url,
595 })))
596 }
597 DistExtension::Source(ext) => {
598 Ok(Self::Source(SourceDist::GitPath(GitPathSourceDist {
599 name,
600 git: Box::new(git),
601 install_path,
602 ext,
603 url,
604 })))
605 }
606 }
607 }
608
609 pub fn from_url(name: PackageName, url: VerbatimParsedUrl) -> Result<Self, Error> {
611 match url.parsed_url {
612 ParsedUrl::Archive(archive) => Self::from_http_url(
613 name,
614 url.verbatim,
615 archive.url,
616 archive.subdirectory,
617 archive.ext,
618 ),
619 ParsedUrl::Path(file) => {
620 Self::from_file_url(name, url.verbatim, &file.install_path, file.ext)
621 }
622 ParsedUrl::Directory(directory) => Self::from_directory_url(
623 name,
624 url.verbatim,
625 &directory.install_path,
626 directory.editable,
627 directory.r#virtual,
628 ),
629 ParsedUrl::GitDirectory(git) => {
630 Self::from_git_directory_url(name, url.verbatim, git.url, git.subdirectory)
631 }
632 ParsedUrl::GitPath(git) => {
633 Self::from_git_path_url(name, url.verbatim, git.url, git.install_path, git.ext)
634 }
635 }
636 }
637
638 fn is_editable(&self) -> bool {
640 match self {
641 Self::Source(dist) => dist.is_editable(),
642 Self::Built(_) => false,
643 }
644 }
645
646 fn is_local(&self) -> bool {
648 match self {
649 Self::Source(dist) => dist.is_local(),
650 Self::Built(dist) => dist.is_local(),
651 }
652 }
653
654 pub fn index(&self) -> Option<&IndexUrl> {
656 match self {
657 Self::Built(dist) => dist.index(),
658 Self::Source(dist) => dist.index(),
659 }
660 }
661
662 pub fn file(&self) -> Option<&File> {
664 match self {
665 Self::Built(built) => built.file(),
666 Self::Source(source) => source.file(),
667 }
668 }
669
670 pub fn source_tree(&self) -> Option<&Path> {
672 match self {
673 Self::Built { .. } => None,
674 Self::Source(source) => source.source_tree(),
675 }
676 }
677
678 pub fn version(&self) -> Option<&Version> {
680 match self {
681 Self::Built(wheel) => Some(wheel.version()),
682 Self::Source(source_dist) => source_dist.version(),
683 }
684 }
685}
686
687impl<'a> From<&'a Dist> for DistRef<'a> {
688 fn from(dist: &'a Dist) -> Self {
689 match dist {
690 Dist::Built(built) => DistRef::Built(built),
691 Dist::Source(source) => DistRef::Source(source),
692 }
693 }
694}
695
696impl<'a> From<&'a SourceDist> for DistRef<'a> {
697 fn from(dist: &'a SourceDist) -> Self {
698 DistRef::Source(dist)
699 }
700}
701
702impl<'a> From<&'a BuiltDist> for DistRef<'a> {
703 fn from(dist: &'a BuiltDist) -> Self {
704 DistRef::Built(dist)
705 }
706}
707
708impl BuiltDist {
709 fn is_local(&self) -> bool {
711 matches!(self, Self::Path(_))
712 }
713
714 pub fn index(&self) -> Option<&IndexUrl> {
716 match self {
717 Self::Registry(registry) => Some(®istry.best_wheel().index),
718 Self::DirectUrl(_) => None,
719 Self::Path(_) => None,
720 Self::GitPath(_) => None,
721 }
722 }
723
724 fn file(&self) -> Option<&File> {
726 match self {
727 Self::Registry(registry) => Some(®istry.best_wheel().file),
728 Self::DirectUrl(_) | Self::Path(_) | Self::GitPath(_) => None,
729 }
730 }
731
732 pub fn version(&self) -> &Version {
733 match self {
734 Self::Registry(wheels) => &wheels.best_wheel().filename.version,
735 Self::DirectUrl(wheel) => &wheel.filename.version,
736 Self::Path(wheel) => &wheel.filename.version,
737 Self::GitPath(wheel) => &wheel.filename.version,
738 }
739 }
740}
741
742impl SourceDist {
743 fn index(&self) -> Option<&IndexUrl> {
745 match self {
746 Self::Registry(registry) => Some(®istry.index),
747 Self::DirectUrl(_)
748 | Self::GitPath(_)
749 | Self::GitDirectory(_)
750 | Self::Path(_)
751 | Self::Directory(_) => None,
752 }
753 }
754
755 fn file(&self) -> Option<&File> {
757 match self {
758 Self::Registry(registry) => Some(®istry.file),
759 Self::DirectUrl(_)
760 | Self::GitPath(_)
761 | Self::GitDirectory(_)
762 | Self::Path(_)
763 | Self::Directory(_) => None,
764 }
765 }
766
767 pub fn version(&self) -> Option<&Version> {
769 match self {
770 Self::Registry(source_dist) => Some(&source_dist.version),
771 Self::DirectUrl(_)
772 | Self::GitPath(_)
773 | Self::GitDirectory(_)
774 | Self::Path(_)
775 | Self::Directory(_) => None,
776 }
777 }
778
779 pub fn is_editable(&self) -> bool {
781 match self {
782 Self::Directory(DirectorySourceDist { editable, .. }) => editable.unwrap_or(false),
783 _ => false,
784 }
785 }
786
787 pub fn is_virtual(&self) -> bool {
789 match self {
790 Self::Directory(DirectorySourceDist { r#virtual, .. }) => r#virtual.unwrap_or(false),
791 _ => false,
792 }
793 }
794
795 pub fn is_first_party(&self) -> bool {
797 match self {
798 Self::Directory(DirectorySourceDist {
799 first_party: FirstParty::Yes,
800 ..
801 }) => true,
802 Self::Directory(DirectorySourceDist {
803 first_party: FirstParty::No,
804 ..
805 })
806 | Self::Registry(_)
807 | Self::DirectUrl(_)
808 | Self::GitDirectory(_)
809 | Self::GitPath(_)
810 | Self::Path(_) => false,
811 }
812 }
813
814 fn is_local(&self) -> bool {
816 matches!(self, Self::Directory(_) | Self::Path(_))
817 }
818
819 pub fn as_path(&self) -> Option<&Path> {
821 match self {
822 Self::Path(dist) => Some(&dist.install_path),
823 Self::Directory(dist) => Some(&dist.install_path),
824 _ => None,
825 }
826 }
827
828 fn source_tree(&self) -> Option<&Path> {
830 match self {
831 Self::Directory(dist) => Some(&dist.install_path),
832 _ => None,
833 }
834 }
835}
836
837impl RegistryBuiltDist {
838 pub fn best_wheel(&self) -> &RegistryBuiltWheel {
840 &self.wheels[self.best_wheel_index]
841 }
842}
843
844impl DirectUrlBuiltDist {
845 pub fn to_parsed_url(&self) -> ParsedUrl {
847 ParsedUrl::Archive(ParsedArchiveUrl::from_source(
848 (*self.location).clone(),
849 None,
850 DistExtension::Wheel,
851 ))
852 }
853}
854
855impl PathBuiltDist {
856 pub fn to_parsed_url(&self) -> ParsedUrl {
858 ParsedUrl::Path(ParsedPathUrl::from_source(
859 self.install_path.clone(),
860 DistExtension::Wheel,
861 self.url.to_url(),
862 ))
863 }
864}
865
866impl PathSourceDist {
867 pub fn to_parsed_url(&self) -> ParsedUrl {
869 ParsedUrl::Path(ParsedPathUrl::from_source(
870 self.install_path.clone(),
871 DistExtension::Source(self.ext),
872 self.url.to_url(),
873 ))
874 }
875}
876
877impl DirectUrlSourceDist {
878 pub fn to_parsed_url(&self) -> ParsedUrl {
880 ParsedUrl::Archive(ParsedArchiveUrl::from_source(
881 (*self.location).clone(),
882 self.subdirectory.clone(),
883 DistExtension::Source(self.ext),
884 ))
885 }
886}
887
888impl GitDirectorySourceDist {
889 pub fn to_parsed_url(&self) -> ParsedUrl {
891 ParsedUrl::GitDirectory(ParsedGitDirectoryUrl::from_source(
892 (*self.git).clone(),
893 self.subdirectory.clone(),
894 ))
895 }
896}
897
898impl GitPathBuiltDist {
899 pub fn to_parsed_url(&self) -> ParsedUrl {
901 ParsedUrl::GitPath(ParsedGitPathUrl::from_source(
902 (*self.git).clone(),
903 self.install_path.clone(),
904 DistExtension::Wheel,
905 ))
906 }
907}
908
909impl GitPathSourceDist {
910 pub fn to_parsed_url(&self) -> ParsedUrl {
912 ParsedUrl::GitPath(ParsedGitPathUrl::from_source(
913 (*self.git).clone(),
914 self.install_path.clone(),
915 DistExtension::Source(self.ext),
916 ))
917 }
918}
919
920impl DirectorySourceDist {
921 pub fn to_parsed_url(&self) -> ParsedUrl {
923 ParsedUrl::Directory(ParsedDirectoryUrl::from_source(
924 self.install_path.clone(),
925 self.editable,
926 self.r#virtual,
927 self.url.to_url(),
928 ))
929 }
930}
931
932impl Name for RegistryBuiltWheel {
933 fn name(&self) -> &PackageName {
934 &self.filename.name
935 }
936}
937
938impl Name for RegistryBuiltDist {
939 fn name(&self) -> &PackageName {
940 self.best_wheel().name()
941 }
942}
943
944impl Name for DirectUrlBuiltDist {
945 fn name(&self) -> &PackageName {
946 &self.filename.name
947 }
948}
949
950impl Name for PathBuiltDist {
951 fn name(&self) -> &PackageName {
952 &self.filename.name
953 }
954}
955
956impl Name for GitPathBuiltDist {
957 fn name(&self) -> &PackageName {
958 &self.filename.name
959 }
960}
961
962impl Name for RegistrySourceDist {
963 fn name(&self) -> &PackageName {
964 &self.name
965 }
966}
967
968impl Name for DirectUrlSourceDist {
969 fn name(&self) -> &PackageName {
970 &self.name
971 }
972}
973
974impl Name for GitPathSourceDist {
975 fn name(&self) -> &PackageName {
976 &self.name
977 }
978}
979
980impl Name for GitDirectorySourceDist {
981 fn name(&self) -> &PackageName {
982 &self.name
983 }
984}
985
986impl Name for PathSourceDist {
987 fn name(&self) -> &PackageName {
988 &self.name
989 }
990}
991
992impl Name for DirectorySourceDist {
993 fn name(&self) -> &PackageName {
994 &self.name
995 }
996}
997
998impl Name for SourceDist {
999 fn name(&self) -> &PackageName {
1000 match self {
1001 Self::Registry(dist) => dist.name(),
1002 Self::DirectUrl(dist) => dist.name(),
1003 Self::GitPath(dist) => dist.name(),
1004 Self::GitDirectory(dist) => dist.name(),
1005 Self::Path(dist) => dist.name(),
1006 Self::Directory(dist) => dist.name(),
1007 }
1008 }
1009}
1010
1011impl Name for BuiltDist {
1012 fn name(&self) -> &PackageName {
1013 match self {
1014 Self::Registry(dist) => dist.name(),
1015 Self::DirectUrl(dist) => dist.name(),
1016 Self::Path(dist) => dist.name(),
1017 Self::GitPath(dist) => dist.name(),
1018 }
1019 }
1020}
1021
1022impl Name for Dist {
1023 fn name(&self) -> &PackageName {
1024 match self {
1025 Self::Built(dist) => dist.name(),
1026 Self::Source(dist) => dist.name(),
1027 }
1028 }
1029}
1030
1031impl Name for CompatibleDist<'_> {
1032 fn name(&self) -> &PackageName {
1033 match self {
1034 Self::InstalledDist(dist) => dist.name(),
1035 Self::SourceDist {
1036 sdist,
1037 prioritized: _,
1038 } => sdist.name(),
1039 Self::CompatibleWheel {
1040 wheel,
1041 priority: _,
1042 prioritized: _,
1043 } => wheel.name(),
1044 Self::IncompatibleWheel {
1045 sdist,
1046 wheel: _,
1047 prioritized: _,
1048 } => sdist.name(),
1049 }
1050 }
1051}
1052
1053impl DistributionMetadata for RegistryBuiltWheel {
1054 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1055 VersionOrUrlRef::Version(&self.filename.version)
1056 }
1057}
1058
1059impl DistributionMetadata for RegistryBuiltDist {
1060 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1061 self.best_wheel().version_or_url()
1062 }
1063}
1064
1065impl DistributionMetadata for DirectUrlBuiltDist {
1066 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1067 VersionOrUrlRef::Url(&self.url)
1068 }
1069
1070 fn version_id(&self) -> VersionId {
1071 VersionId::from_archive(self.location.as_ref().clone(), None)
1072 }
1073}
1074
1075impl DistributionMetadata for PathBuiltDist {
1076 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1077 VersionOrUrlRef::Url(&self.url)
1078 }
1079
1080 fn version_id(&self) -> VersionId {
1081 VersionId::from_path(self.install_path.as_ref())
1082 }
1083}
1084
1085impl DistributionMetadata for GitPathBuiltDist {
1086 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1087 VersionOrUrlRef::Url(&self.url)
1088 }
1089}
1090
1091impl DistributionMetadata for RegistrySourceDist {
1092 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1093 VersionOrUrlRef::Version(&self.version)
1094 }
1095}
1096
1097impl DistributionMetadata for DirectUrlSourceDist {
1098 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1099 VersionOrUrlRef::Url(&self.url)
1100 }
1101
1102 fn version_id(&self) -> VersionId {
1103 VersionId::from_archive(
1104 self.location.as_ref().clone(),
1105 self.subdirectory.clone().map(Path::into_path_buf),
1106 )
1107 }
1108}
1109
1110impl DistributionMetadata for GitPathSourceDist {
1111 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1112 VersionOrUrlRef::Url(&self.url)
1113 }
1114
1115 fn version_id(&self) -> VersionId {
1116 VersionId::from_git(self.git.as_ref(), Some(&self.install_path))
1117 }
1118}
1119
1120impl DistributionMetadata for GitDirectorySourceDist {
1121 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1122 VersionOrUrlRef::Url(&self.url)
1123 }
1124
1125 fn version_id(&self) -> VersionId {
1126 VersionId::from_git(self.git.as_ref(), self.subdirectory.as_deref())
1127 }
1128}
1129
1130impl DistributionMetadata for PathSourceDist {
1131 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1132 VersionOrUrlRef::Url(&self.url)
1133 }
1134
1135 fn version_id(&self) -> VersionId {
1136 VersionId::from_path(self.install_path.as_ref())
1137 }
1138}
1139
1140impl DistributionMetadata for DirectorySourceDist {
1141 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1142 VersionOrUrlRef::Url(&self.url)
1143 }
1144
1145 fn version_id(&self) -> VersionId {
1146 VersionId::from_directory(self.install_path.as_ref())
1147 }
1148}
1149
1150impl DistributionMetadata for SourceDist {
1151 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1152 match self {
1153 Self::Registry(dist) => dist.version_or_url(),
1154 Self::DirectUrl(dist) => dist.version_or_url(),
1155 Self::GitPath(dist) => dist.version_or_url(),
1156 Self::GitDirectory(dist) => dist.version_or_url(),
1157 Self::Path(dist) => dist.version_or_url(),
1158 Self::Directory(dist) => dist.version_or_url(),
1159 }
1160 }
1161
1162 fn version_id(&self) -> VersionId {
1163 match self {
1164 Self::Registry(dist) => dist.version_id(),
1165 Self::DirectUrl(dist) => dist.version_id(),
1166 Self::GitPath(dist) => dist.version_id(),
1167 Self::GitDirectory(dist) => dist.version_id(),
1168 Self::Path(dist) => dist.version_id(),
1169 Self::Directory(dist) => dist.version_id(),
1170 }
1171 }
1172}
1173
1174impl DistributionMetadata for BuiltDist {
1175 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1176 match self {
1177 Self::Registry(dist) => dist.version_or_url(),
1178 Self::DirectUrl(dist) => dist.version_or_url(),
1179 Self::Path(dist) => dist.version_or_url(),
1180 Self::GitPath(dist) => dist.version_or_url(),
1181 }
1182 }
1183
1184 fn version_id(&self) -> VersionId {
1185 match self {
1186 Self::Registry(dist) => dist.version_id(),
1187 Self::DirectUrl(dist) => dist.version_id(),
1188 Self::Path(dist) => dist.version_id(),
1189 Self::GitPath(dist) => dist.version_id(),
1190 }
1191 }
1192}
1193
1194impl DistributionMetadata for Dist {
1195 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1196 match self {
1197 Self::Built(dist) => dist.version_or_url(),
1198 Self::Source(dist) => dist.version_or_url(),
1199 }
1200 }
1201
1202 fn version_id(&self) -> VersionId {
1203 match self {
1204 Self::Built(dist) => dist.version_id(),
1205 Self::Source(dist) => dist.version_id(),
1206 }
1207 }
1208}
1209
1210impl RemoteSource for File {
1211 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1212 Ok(Cow::Borrowed(&self.filename))
1213 }
1214
1215 fn size(&self) -> Option<u64> {
1216 self.size
1217 }
1218}
1219
1220impl RemoteSource for Url {
1221 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1222 let mut path_segments = self
1224 .path_segments()
1225 .ok_or_else(|| Error::MissingPathSegments(self.to_string()))?;
1226
1227 let last = path_segments
1229 .next_back()
1230 .expect("path segments is non-empty");
1231
1232 let filename = percent_encoding::percent_decode_str(last).decode_utf8()?;
1234
1235 Ok(filename)
1236 }
1237
1238 fn size(&self) -> Option<u64> {
1239 None
1240 }
1241}
1242
1243impl RemoteSource for UrlString {
1244 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1245 let url = self.as_ref();
1246 if memchr3(b'?', b'#', b'%', url.as_bytes()).is_none()
1247 && let Some((_, filename)) = url.rsplit_once('/')
1248 {
1249 return Ok(Cow::Borrowed(filename));
1250 }
1251
1252 let last = self
1254 .base_str()
1255 .split('/')
1256 .next_back()
1257 .ok_or_else(|| Error::MissingPathSegments(self.to_string()))?;
1258
1259 let filename = percent_encoding::percent_decode_str(last).decode_utf8()?;
1261
1262 Ok(filename)
1263 }
1264
1265 fn size(&self) -> Option<u64> {
1266 None
1267 }
1268}
1269
1270impl RemoteSource for RegistryBuiltWheel {
1271 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1272 self.file.filename()
1273 }
1274
1275 fn size(&self) -> Option<u64> {
1276 self.file.size()
1277 }
1278}
1279
1280impl RemoteSource for RegistryBuiltDist {
1281 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1282 self.best_wheel().filename()
1283 }
1284
1285 fn size(&self) -> Option<u64> {
1286 self.best_wheel().size()
1287 }
1288}
1289
1290impl RemoteSource for RegistrySourceDist {
1291 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1292 self.file.filename()
1293 }
1294
1295 fn size(&self) -> Option<u64> {
1296 self.file.size()
1297 }
1298}
1299
1300impl RemoteSource for DirectUrlBuiltDist {
1301 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1302 self.url.filename()
1303 }
1304
1305 fn size(&self) -> Option<u64> {
1306 self.size
1307 }
1308}
1309
1310impl RemoteSource for DirectUrlSourceDist {
1311 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1312 self.url.filename()
1313 }
1314
1315 fn size(&self) -> Option<u64> {
1316 self.size
1317 }
1318}
1319
1320impl RemoteSource for GitPathSourceDist {
1321 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1322 match self.url.filename()? {
1324 Cow::Borrowed(filename) if let Some((_, suffix)) = filename.rsplit_once('@') => {
1325 Ok(Cow::Borrowed(suffix))
1326 }
1327 Cow::Owned(ref filename) if let Some((_, suffix)) = filename.rsplit_once('@') => {
1328 Ok(Cow::Owned(suffix.to_owned()))
1329 }
1330 filename => Ok(filename),
1331 }
1332 }
1333
1334 fn size(&self) -> Option<u64> {
1335 self.url.size()
1336 }
1337}
1338
1339impl RemoteSource for GitDirectorySourceDist {
1340 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1341 match self.url.filename()? {
1343 Cow::Borrowed(filename) if let Some((_, suffix)) = filename.rsplit_once('@') => {
1344 Ok(Cow::Borrowed(suffix))
1345 }
1346 Cow::Owned(ref filename) if let Some((_, suffix)) = filename.rsplit_once('@') => {
1347 Ok(Cow::Owned(suffix.to_owned()))
1348 }
1349 filename => Ok(filename),
1350 }
1351 }
1352
1353 fn size(&self) -> Option<u64> {
1354 self.url.size()
1355 }
1356}
1357
1358impl RemoteSource for PathBuiltDist {
1359 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1360 self.url.filename()
1361 }
1362
1363 fn size(&self) -> Option<u64> {
1364 self.url.size()
1365 }
1366}
1367
1368impl RemoteSource for GitPathBuiltDist {
1369 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1370 self.url.filename()
1371 }
1372
1373 fn size(&self) -> Option<u64> {
1374 self.url.size()
1375 }
1376}
1377
1378impl RemoteSource for PathSourceDist {
1379 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1380 self.url.filename()
1381 }
1382
1383 fn size(&self) -> Option<u64> {
1384 self.url.size()
1385 }
1386}
1387
1388impl RemoteSource for DirectorySourceDist {
1389 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1390 self.url.filename()
1391 }
1392
1393 fn size(&self) -> Option<u64> {
1394 self.url.size()
1395 }
1396}
1397
1398impl RemoteSource for SourceDist {
1399 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1400 match self {
1401 Self::Registry(dist) => dist.filename(),
1402 Self::DirectUrl(dist) => dist.filename(),
1403 Self::GitPath(dist) => dist.filename(),
1404 Self::GitDirectory(dist) => dist.filename(),
1405 Self::Path(dist) => dist.filename(),
1406 Self::Directory(dist) => dist.filename(),
1407 }
1408 }
1409
1410 fn size(&self) -> Option<u64> {
1411 match self {
1412 Self::Registry(dist) => dist.size(),
1413 Self::DirectUrl(dist) => dist.size(),
1414 Self::GitPath(dist) => dist.size(),
1415 Self::GitDirectory(dist) => dist.size(),
1416 Self::Path(dist) => dist.size(),
1417 Self::Directory(dist) => dist.size(),
1418 }
1419 }
1420}
1421
1422impl RemoteSource for BuiltDist {
1423 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1424 match self {
1425 Self::Registry(dist) => dist.filename(),
1426 Self::DirectUrl(dist) => dist.filename(),
1427 Self::Path(dist) => dist.filename(),
1428 Self::GitPath(dist) => dist.filename(),
1429 }
1430 }
1431
1432 fn size(&self) -> Option<u64> {
1433 match self {
1434 Self::Registry(dist) => dist.size(),
1435 Self::DirectUrl(dist) => dist.size(),
1436 Self::Path(dist) => dist.size(),
1437 Self::GitPath(dist) => dist.size(),
1438 }
1439 }
1440}
1441
1442impl RemoteSource for Dist {
1443 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1444 match self {
1445 Self::Built(dist) => dist.filename(),
1446 Self::Source(dist) => dist.filename(),
1447 }
1448 }
1449
1450 fn size(&self) -> Option<u64> {
1451 match self {
1452 Self::Built(dist) => dist.size(),
1453 Self::Source(dist) => dist.size(),
1454 }
1455 }
1456}
1457
1458impl Identifier for DisplaySafeUrl {
1459 fn distribution_id(&self) -> DistributionId {
1460 DistributionId::Url(uv_cache_key::CanonicalUrl::new(self.clone()))
1461 }
1462
1463 fn resource_id(&self) -> ResourceId {
1464 ResourceId::Url(uv_cache_key::RepositoryUrl::new(self.clone()))
1465 }
1466}
1467
1468impl Identifier for File {
1469 fn distribution_id(&self) -> DistributionId {
1470 self.hashes
1471 .first()
1472 .cloned()
1473 .map(DistributionId::Digest)
1474 .unwrap_or_else(|| self.url.distribution_id())
1475 }
1476
1477 fn resource_id(&self) -> ResourceId {
1478 self.hashes
1479 .first()
1480 .cloned()
1481 .map(ResourceId::Digest)
1482 .unwrap_or_else(|| self.url.resource_id())
1483 }
1484}
1485
1486impl Identifier for Path {
1487 fn distribution_id(&self) -> DistributionId {
1488 DistributionId::PathBuf(self.to_path_buf())
1489 }
1490
1491 fn resource_id(&self) -> ResourceId {
1492 ResourceId::PathBuf(self.to_path_buf())
1493 }
1494}
1495
1496impl Identifier for FileLocation {
1497 fn distribution_id(&self) -> DistributionId {
1498 match self {
1499 Self::RelativeUrl(base, url) => {
1500 DistributionId::RelativeUrl(base.to_string(), url.to_string())
1501 }
1502 Self::AbsoluteUrl(url) => DistributionId::AbsoluteUrl(url.to_string()),
1503 }
1504 }
1505
1506 fn resource_id(&self) -> ResourceId {
1507 match self {
1508 Self::RelativeUrl(base, url) => {
1509 ResourceId::RelativeUrl(base.to_string(), url.to_string())
1510 }
1511 Self::AbsoluteUrl(url) => ResourceId::AbsoluteUrl(url.to_string()),
1512 }
1513 }
1514}
1515
1516impl Identifier for RegistryBuiltWheel {
1517 fn distribution_id(&self) -> DistributionId {
1518 self.file.distribution_id()
1519 }
1520
1521 fn resource_id(&self) -> ResourceId {
1522 self.file.resource_id()
1523 }
1524}
1525
1526impl Identifier for RegistryBuiltDist {
1527 fn distribution_id(&self) -> DistributionId {
1528 self.best_wheel().distribution_id()
1529 }
1530
1531 fn resource_id(&self) -> ResourceId {
1532 self.best_wheel().resource_id()
1533 }
1534}
1535
1536impl Identifier for RegistrySourceDist {
1537 fn distribution_id(&self) -> DistributionId {
1538 self.file.distribution_id()
1539 }
1540
1541 fn resource_id(&self) -> ResourceId {
1542 self.file.resource_id()
1543 }
1544}
1545
1546impl Identifier for DirectUrlBuiltDist {
1547 fn distribution_id(&self) -> DistributionId {
1548 self.url.distribution_id()
1549 }
1550
1551 fn resource_id(&self) -> ResourceId {
1552 self.url.resource_id()
1553 }
1554}
1555
1556impl Identifier for DirectUrlSourceDist {
1557 fn distribution_id(&self) -> DistributionId {
1558 self.url.distribution_id()
1559 }
1560
1561 fn resource_id(&self) -> ResourceId {
1562 self.url.resource_id()
1563 }
1564}
1565
1566impl Identifier for PathBuiltDist {
1567 fn distribution_id(&self) -> DistributionId {
1568 self.url.distribution_id()
1569 }
1570
1571 fn resource_id(&self) -> ResourceId {
1572 self.url.resource_id()
1573 }
1574}
1575
1576impl Identifier for GitPathBuiltDist {
1577 fn distribution_id(&self) -> DistributionId {
1578 self.url.distribution_id()
1579 }
1580
1581 fn resource_id(&self) -> ResourceId {
1582 self.url.resource_id()
1583 }
1584}
1585
1586impl Identifier for PathSourceDist {
1587 fn distribution_id(&self) -> DistributionId {
1588 self.url.distribution_id()
1589 }
1590
1591 fn resource_id(&self) -> ResourceId {
1592 self.url.resource_id()
1593 }
1594}
1595
1596impl Identifier for DirectorySourceDist {
1597 fn distribution_id(&self) -> DistributionId {
1598 self.url.distribution_id()
1599 }
1600
1601 fn resource_id(&self) -> ResourceId {
1602 self.url.resource_id()
1603 }
1604}
1605
1606impl Identifier for GitPathSourceDist {
1607 fn distribution_id(&self) -> DistributionId {
1608 self.url.distribution_id()
1609 }
1610
1611 fn resource_id(&self) -> ResourceId {
1612 self.url.resource_id()
1613 }
1614}
1615
1616impl Identifier for GitDirectorySourceDist {
1617 fn distribution_id(&self) -> DistributionId {
1618 self.url.distribution_id()
1619 }
1620
1621 fn resource_id(&self) -> ResourceId {
1622 self.url.resource_id()
1623 }
1624}
1625
1626impl Identifier for SourceDist {
1627 fn distribution_id(&self) -> DistributionId {
1628 match self {
1629 Self::Registry(dist) => dist.distribution_id(),
1630 Self::DirectUrl(dist) => dist.distribution_id(),
1631 Self::GitPath(dist) => dist.distribution_id(),
1632 Self::GitDirectory(dist) => dist.distribution_id(),
1633 Self::Path(dist) => dist.distribution_id(),
1634 Self::Directory(dist) => dist.distribution_id(),
1635 }
1636 }
1637
1638 fn resource_id(&self) -> ResourceId {
1639 match self {
1640 Self::Registry(dist) => dist.resource_id(),
1641 Self::DirectUrl(dist) => dist.resource_id(),
1642 Self::GitPath(dist) => dist.resource_id(),
1643 Self::GitDirectory(dist) => dist.resource_id(),
1644 Self::Path(dist) => dist.resource_id(),
1645 Self::Directory(dist) => dist.resource_id(),
1646 }
1647 }
1648}
1649
1650impl Identifier for BuiltDist {
1651 fn distribution_id(&self) -> DistributionId {
1652 match self {
1653 Self::Registry(dist) => dist.distribution_id(),
1654 Self::DirectUrl(dist) => dist.distribution_id(),
1655 Self::Path(dist) => dist.distribution_id(),
1656 Self::GitPath(dist) => dist.distribution_id(),
1657 }
1658 }
1659
1660 fn resource_id(&self) -> ResourceId {
1661 match self {
1662 Self::Registry(dist) => dist.resource_id(),
1663 Self::DirectUrl(dist) => dist.resource_id(),
1664 Self::Path(dist) => dist.resource_id(),
1665 Self::GitPath(dist) => dist.resource_id(),
1666 }
1667 }
1668}
1669
1670impl Identifier for InstalledDist {
1671 fn distribution_id(&self) -> DistributionId {
1672 self.install_path().distribution_id()
1673 }
1674
1675 fn resource_id(&self) -> ResourceId {
1676 self.install_path().resource_id()
1677 }
1678}
1679
1680impl Identifier for Dist {
1681 fn distribution_id(&self) -> DistributionId {
1682 match self {
1683 Self::Built(dist) => dist.distribution_id(),
1684 Self::Source(dist) => dist.distribution_id(),
1685 }
1686 }
1687
1688 fn resource_id(&self) -> ResourceId {
1689 match self {
1690 Self::Built(dist) => dist.resource_id(),
1691 Self::Source(dist) => dist.resource_id(),
1692 }
1693 }
1694}
1695
1696impl Identifier for DirectSourceUrl<'_> {
1697 fn distribution_id(&self) -> DistributionId {
1698 self.url.distribution_id()
1699 }
1700
1701 fn resource_id(&self) -> ResourceId {
1702 self.url.resource_id()
1703 }
1704}
1705
1706impl Identifier for GitDirectorySourceUrl<'_> {
1707 fn distribution_id(&self) -> DistributionId {
1708 self.url.distribution_id()
1709 }
1710
1711 fn resource_id(&self) -> ResourceId {
1712 self.url.resource_id()
1713 }
1714}
1715
1716impl Identifier for GitPathSourceUrl<'_> {
1717 fn distribution_id(&self) -> DistributionId {
1718 self.url.distribution_id()
1719 }
1720
1721 fn resource_id(&self) -> ResourceId {
1722 self.url.resource_id()
1723 }
1724}
1725
1726impl Identifier for PathSourceUrl<'_> {
1727 fn distribution_id(&self) -> DistributionId {
1728 self.url.distribution_id()
1729 }
1730
1731 fn resource_id(&self) -> ResourceId {
1732 self.url.resource_id()
1733 }
1734}
1735
1736impl Identifier for DirectorySourceUrl<'_> {
1737 fn distribution_id(&self) -> DistributionId {
1738 self.url.distribution_id()
1739 }
1740
1741 fn resource_id(&self) -> ResourceId {
1742 self.url.resource_id()
1743 }
1744}
1745
1746impl Identifier for SourceUrl<'_> {
1747 fn distribution_id(&self) -> DistributionId {
1748 match self {
1749 Self::Direct(url) => url.distribution_id(),
1750 Self::GitDirectory(url) => url.distribution_id(),
1751 Self::GitPath(url) => url.distribution_id(),
1752 Self::Path(url) => url.distribution_id(),
1753 Self::Directory(url) => url.distribution_id(),
1754 }
1755 }
1756
1757 fn resource_id(&self) -> ResourceId {
1758 match self {
1759 Self::Direct(url) => url.resource_id(),
1760 Self::GitDirectory(url) => url.resource_id(),
1761 Self::GitPath(url) => url.resource_id(),
1762 Self::Path(url) => url.resource_id(),
1763 Self::Directory(url) => url.resource_id(),
1764 }
1765 }
1766}
1767
1768impl Identifier for BuildableSource<'_> {
1769 fn distribution_id(&self) -> DistributionId {
1770 match self {
1771 Self::Dist(source) => source.distribution_id(),
1772 Self::Url(source) => source.distribution_id(),
1773 }
1774 }
1775
1776 fn resource_id(&self) -> ResourceId {
1777 match self {
1778 Self::Dist(source) => source.resource_id(),
1779 Self::Url(source) => source.resource_id(),
1780 }
1781 }
1782}
1783
1784#[cfg(test)]
1785mod test {
1786 use crate::{BuiltDist, Dist, RemoteSource, SourceDist, UrlString};
1787 use uv_redacted::DisplaySafeUrl;
1788
1789 #[test]
1791 fn dist_size() {
1792 assert!(size_of::<Dist>() <= 200, "{}", size_of::<Dist>());
1793 assert!(size_of::<BuiltDist>() <= 200, "{}", size_of::<BuiltDist>());
1794 assert!(
1795 size_of::<SourceDist>() <= 176,
1796 "{}",
1797 size_of::<SourceDist>()
1798 );
1799 }
1800
1801 #[test]
1802 fn remote_source() {
1803 for url in [
1804 "https://example.com/foo-0.1.0.tar.gz",
1805 "https://example.com/foo%2D0.1.0.tar.gz",
1806 "https://example.com/foo-0.1.0.tar.gz#fragment",
1807 "https://example.com/foo-0.1.0.tar.gz?query",
1808 "https://example.com/foo-0.1.0.tar.gz?query#fragment",
1809 "https://example.com/foo-0.1.0.tar.gz?query=1/2#fragment",
1810 "https://example.com/foo-0.1.0.tar.gz?query=1/2#fragment/3",
1811 "https://example.com/foo%2D0.1.0.tar.gz?query=1/2#fragment/3",
1812 ] {
1813 let url = DisplaySafeUrl::parse(url).unwrap();
1814 assert_eq!(url.filename().unwrap(), "foo-0.1.0.tar.gz", "{url}");
1815 let url = UrlString::from(url.clone());
1816 assert_eq!(url.filename().unwrap(), "foo-0.1.0.tar.gz", "{url}");
1817 }
1818 }
1819}