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, Hash, PartialEq, Eq)]
388pub struct DirectorySourceDist {
389 pub name: PackageName,
390 pub install_path: Box<Path>,
392 pub editable: Option<bool>,
394 pub r#virtual: Option<bool>,
396 pub url: VerbatimUrl,
398}
399
400impl Dist {
401 pub fn from_http_url(
404 name: PackageName,
405 url: VerbatimUrl,
406 location: DisplaySafeUrl,
407 subdirectory: Option<Box<Path>>,
408 ext: DistExtension,
409 ) -> Result<Self, Error> {
410 match ext {
411 DistExtension::Wheel => {
412 let filename = WheelFilename::from_str(&url.filename()?)?;
414 if filename.name != name {
415 return Err(Error::PackageNameMismatch(
416 name,
417 filename.name,
418 url.verbatim().to_string(),
419 ));
420 }
421
422 Ok(Self::Built(BuiltDist::DirectUrl(DirectUrlBuiltDist {
423 filename,
424 location: Box::new(location),
425 url,
426 size: None,
427 })))
428 }
429 DistExtension::Source(ext) => {
430 if !ext.is_pep625_compliant() {
431 return Err(Error::NotPep625Filename(url.verbatim().to_string()));
432 }
433 Ok(Self::Source(SourceDist::DirectUrl(DirectUrlSourceDist {
434 name,
435 location: Box::new(location),
436 subdirectory,
437 ext,
438 url,
439 size: None,
440 })))
441 }
442 }
443 }
444
445 pub fn from_file_url(
447 name: PackageName,
448 url: VerbatimUrl,
449 install_path: &Path,
450 ext: DistExtension,
451 ) -> Result<Self, Error> {
452 let install_path = path::absolute(install_path)?;
454
455 let install_path = normalize_absolute_path(&install_path)?;
457
458 if !install_path.exists() {
460 return Err(Error::NotFound(url.to_url()));
461 }
462
463 match ext {
465 DistExtension::Wheel => {
466 let filename = install_path
468 .file_name()
469 .and_then(OsStr::to_str)
470 .ok_or_else(|| Error::MissingWheelFilename(install_path.clone()))?;
471 let filename = WheelFilename::from_str(filename)?;
472 if filename.name != name {
473 return Err(Error::PackageNameMismatch(
474 name,
475 filename.name,
476 url.verbatim().to_string(),
477 ));
478 }
479 Ok(Self::Built(BuiltDist::Path(PathBuiltDist {
480 filename,
481 install_path: install_path.into_boxed_path(),
482 url,
483 })))
484 }
485 DistExtension::Source(ext) => {
486 if !ext.is_pep625_compliant() {
487 return Err(Error::NotPep625Filename(url.verbatim().to_string()));
488 }
489
490 let version = url
492 .filename()
493 .ok()
494 .and_then(|filename| {
495 SourceDistFilename::parse(filename.as_ref(), ext, &name).ok()
496 })
497 .map(|filename| filename.version);
498
499 Ok(Self::Source(SourceDist::Path(PathSourceDist {
500 name,
501 version,
502 install_path: install_path.into_boxed_path(),
503 ext,
504 url,
505 })))
506 }
507 }
508 }
509
510 pub fn from_directory_url(
512 name: PackageName,
513 url: VerbatimUrl,
514 install_path: &Path,
515 editable: Option<bool>,
516 r#virtual: Option<bool>,
517 ) -> Result<Self, Error> {
518 let install_path = path::absolute(install_path)?;
520
521 let install_path = normalize_absolute_path(&install_path)?;
523
524 if !install_path.exists() {
526 return Err(Error::NotFound(url.to_url()));
527 }
528
529 Ok(Self::Source(SourceDist::Directory(DirectorySourceDist {
531 name,
532 install_path: install_path.into_boxed_path(),
533 editable,
534 r#virtual,
535 url,
536 })))
537 }
538
539 pub fn from_git_directory_url(
541 name: PackageName,
542 url: VerbatimUrl,
543 git: GitUrl,
544 subdirectory: Option<Box<Path>>,
545 ) -> Result<Self, Error> {
546 Ok(Self::Source(SourceDist::GitDirectory(
547 GitDirectorySourceDist {
548 name,
549 git: Box::new(git),
550 subdirectory,
551 url,
552 },
553 )))
554 }
555
556 pub fn from_git_path_url(
558 name: PackageName,
559 url: VerbatimUrl,
560 git: GitUrl,
561 install_path: PathBuf,
562 ext: DistExtension,
563 ) -> Result<Self, Error> {
564 match ext {
565 DistExtension::Wheel => {
566 let filename = install_path
568 .file_name()
569 .and_then(OsStr::to_str)
570 .ok_or_else(|| Error::MissingWheelFilename(install_path.clone()))?;
571 let filename = WheelFilename::from_str(filename)?;
572 if filename.name != name {
573 return Err(Error::PackageNameMismatch(
574 name,
575 filename.name,
576 url.verbatim().to_string(),
577 ));
578 }
579
580 Ok(Self::Built(BuiltDist::GitPath(GitPathBuiltDist {
581 filename,
582 git: Box::new(git),
583 install_path,
584 url,
585 })))
586 }
587 DistExtension::Source(ext) => {
588 Ok(Self::Source(SourceDist::GitPath(GitPathSourceDist {
589 name,
590 git: Box::new(git),
591 install_path,
592 ext,
593 url,
594 })))
595 }
596 }
597 }
598
599 pub fn from_url(name: PackageName, url: VerbatimParsedUrl) -> Result<Self, Error> {
601 match url.parsed_url {
602 ParsedUrl::Archive(archive) => Self::from_http_url(
603 name,
604 url.verbatim,
605 archive.url,
606 archive.subdirectory,
607 archive.ext,
608 ),
609 ParsedUrl::Path(file) => {
610 Self::from_file_url(name, url.verbatim, &file.install_path, file.ext)
611 }
612 ParsedUrl::Directory(directory) => Self::from_directory_url(
613 name,
614 url.verbatim,
615 &directory.install_path,
616 directory.editable,
617 directory.r#virtual,
618 ),
619 ParsedUrl::GitDirectory(git) => {
620 Self::from_git_directory_url(name, url.verbatim, git.url, git.subdirectory)
621 }
622 ParsedUrl::GitPath(git) => {
623 Self::from_git_path_url(name, url.verbatim, git.url, git.install_path, git.ext)
624 }
625 }
626 }
627
628 fn is_editable(&self) -> bool {
630 match self {
631 Self::Source(dist) => dist.is_editable(),
632 Self::Built(_) => false,
633 }
634 }
635
636 fn is_local(&self) -> bool {
638 match self {
639 Self::Source(dist) => dist.is_local(),
640 Self::Built(dist) => dist.is_local(),
641 }
642 }
643
644 pub fn index(&self) -> Option<&IndexUrl> {
646 match self {
647 Self::Built(dist) => dist.index(),
648 Self::Source(dist) => dist.index(),
649 }
650 }
651
652 pub fn file(&self) -> Option<&File> {
654 match self {
655 Self::Built(built) => built.file(),
656 Self::Source(source) => source.file(),
657 }
658 }
659
660 pub fn source_tree(&self) -> Option<&Path> {
662 match self {
663 Self::Built { .. } => None,
664 Self::Source(source) => source.source_tree(),
665 }
666 }
667
668 pub fn version(&self) -> Option<&Version> {
670 match self {
671 Self::Built(wheel) => Some(wheel.version()),
672 Self::Source(source_dist) => source_dist.version(),
673 }
674 }
675}
676
677impl<'a> From<&'a Dist> for DistRef<'a> {
678 fn from(dist: &'a Dist) -> Self {
679 match dist {
680 Dist::Built(built) => DistRef::Built(built),
681 Dist::Source(source) => DistRef::Source(source),
682 }
683 }
684}
685
686impl<'a> From<&'a SourceDist> for DistRef<'a> {
687 fn from(dist: &'a SourceDist) -> Self {
688 DistRef::Source(dist)
689 }
690}
691
692impl<'a> From<&'a BuiltDist> for DistRef<'a> {
693 fn from(dist: &'a BuiltDist) -> Self {
694 DistRef::Built(dist)
695 }
696}
697
698impl BuiltDist {
699 fn is_local(&self) -> bool {
701 matches!(self, Self::Path(_))
702 }
703
704 pub fn index(&self) -> Option<&IndexUrl> {
706 match self {
707 Self::Registry(registry) => Some(®istry.best_wheel().index),
708 Self::DirectUrl(_) => None,
709 Self::Path(_) => None,
710 Self::GitPath(_) => None,
711 }
712 }
713
714 fn file(&self) -> Option<&File> {
716 match self {
717 Self::Registry(registry) => Some(®istry.best_wheel().file),
718 Self::DirectUrl(_) | Self::Path(_) | Self::GitPath(_) => None,
719 }
720 }
721
722 pub fn version(&self) -> &Version {
723 match self {
724 Self::Registry(wheels) => &wheels.best_wheel().filename.version,
725 Self::DirectUrl(wheel) => &wheel.filename.version,
726 Self::Path(wheel) => &wheel.filename.version,
727 Self::GitPath(wheel) => &wheel.filename.version,
728 }
729 }
730}
731
732impl SourceDist {
733 fn index(&self) -> Option<&IndexUrl> {
735 match self {
736 Self::Registry(registry) => Some(®istry.index),
737 Self::DirectUrl(_)
738 | Self::GitPath(_)
739 | Self::GitDirectory(_)
740 | Self::Path(_)
741 | Self::Directory(_) => None,
742 }
743 }
744
745 fn file(&self) -> Option<&File> {
747 match self {
748 Self::Registry(registry) => Some(®istry.file),
749 Self::DirectUrl(_)
750 | Self::GitPath(_)
751 | Self::GitDirectory(_)
752 | Self::Path(_)
753 | Self::Directory(_) => None,
754 }
755 }
756
757 pub fn version(&self) -> Option<&Version> {
759 match self {
760 Self::Registry(source_dist) => Some(&source_dist.version),
761 Self::DirectUrl(_)
762 | Self::GitPath(_)
763 | Self::GitDirectory(_)
764 | Self::Path(_)
765 | Self::Directory(_) => None,
766 }
767 }
768
769 pub fn is_editable(&self) -> bool {
771 match self {
772 Self::Directory(DirectorySourceDist { editable, .. }) => editable.unwrap_or(false),
773 _ => false,
774 }
775 }
776
777 pub fn is_virtual(&self) -> bool {
779 match self {
780 Self::Directory(DirectorySourceDist { r#virtual, .. }) => r#virtual.unwrap_or(false),
781 _ => false,
782 }
783 }
784
785 fn is_local(&self) -> bool {
787 matches!(self, Self::Directory(_) | Self::Path(_))
788 }
789
790 pub fn as_path(&self) -> Option<&Path> {
792 match self {
793 Self::Path(dist) => Some(&dist.install_path),
794 Self::Directory(dist) => Some(&dist.install_path),
795 _ => None,
796 }
797 }
798
799 fn source_tree(&self) -> Option<&Path> {
801 match self {
802 Self::Directory(dist) => Some(&dist.install_path),
803 _ => None,
804 }
805 }
806}
807
808impl RegistryBuiltDist {
809 pub fn best_wheel(&self) -> &RegistryBuiltWheel {
811 &self.wheels[self.best_wheel_index]
812 }
813}
814
815impl DirectUrlBuiltDist {
816 pub fn to_parsed_url(&self) -> ParsedUrl {
818 ParsedUrl::Archive(ParsedArchiveUrl::from_source(
819 (*self.location).clone(),
820 None,
821 DistExtension::Wheel,
822 ))
823 }
824}
825
826impl PathBuiltDist {
827 pub fn to_parsed_url(&self) -> ParsedUrl {
829 ParsedUrl::Path(ParsedPathUrl::from_source(
830 self.install_path.clone(),
831 DistExtension::Wheel,
832 self.url.to_url(),
833 ))
834 }
835}
836
837impl PathSourceDist {
838 pub fn to_parsed_url(&self) -> ParsedUrl {
840 ParsedUrl::Path(ParsedPathUrl::from_source(
841 self.install_path.clone(),
842 DistExtension::Source(self.ext),
843 self.url.to_url(),
844 ))
845 }
846}
847
848impl DirectUrlSourceDist {
849 pub fn to_parsed_url(&self) -> ParsedUrl {
851 ParsedUrl::Archive(ParsedArchiveUrl::from_source(
852 (*self.location).clone(),
853 self.subdirectory.clone(),
854 DistExtension::Source(self.ext),
855 ))
856 }
857}
858
859impl GitDirectorySourceDist {
860 pub fn to_parsed_url(&self) -> ParsedUrl {
862 ParsedUrl::GitDirectory(ParsedGitDirectoryUrl::from_source(
863 (*self.git).clone(),
864 self.subdirectory.clone(),
865 ))
866 }
867}
868
869impl GitPathBuiltDist {
870 pub fn to_parsed_url(&self) -> ParsedUrl {
872 ParsedUrl::GitPath(ParsedGitPathUrl::from_source(
873 (*self.git).clone(),
874 self.install_path.clone(),
875 DistExtension::Wheel,
876 ))
877 }
878}
879
880impl GitPathSourceDist {
881 pub fn to_parsed_url(&self) -> ParsedUrl {
883 ParsedUrl::GitPath(ParsedGitPathUrl::from_source(
884 (*self.git).clone(),
885 self.install_path.clone(),
886 DistExtension::Source(self.ext),
887 ))
888 }
889}
890
891impl DirectorySourceDist {
892 pub fn to_parsed_url(&self) -> ParsedUrl {
894 ParsedUrl::Directory(ParsedDirectoryUrl::from_source(
895 self.install_path.clone(),
896 self.editable,
897 self.r#virtual,
898 self.url.to_url(),
899 ))
900 }
901}
902
903impl Name for RegistryBuiltWheel {
904 fn name(&self) -> &PackageName {
905 &self.filename.name
906 }
907}
908
909impl Name for RegistryBuiltDist {
910 fn name(&self) -> &PackageName {
911 self.best_wheel().name()
912 }
913}
914
915impl Name for DirectUrlBuiltDist {
916 fn name(&self) -> &PackageName {
917 &self.filename.name
918 }
919}
920
921impl Name for PathBuiltDist {
922 fn name(&self) -> &PackageName {
923 &self.filename.name
924 }
925}
926
927impl Name for GitPathBuiltDist {
928 fn name(&self) -> &PackageName {
929 &self.filename.name
930 }
931}
932
933impl Name for RegistrySourceDist {
934 fn name(&self) -> &PackageName {
935 &self.name
936 }
937}
938
939impl Name for DirectUrlSourceDist {
940 fn name(&self) -> &PackageName {
941 &self.name
942 }
943}
944
945impl Name for GitPathSourceDist {
946 fn name(&self) -> &PackageName {
947 &self.name
948 }
949}
950
951impl Name for GitDirectorySourceDist {
952 fn name(&self) -> &PackageName {
953 &self.name
954 }
955}
956
957impl Name for PathSourceDist {
958 fn name(&self) -> &PackageName {
959 &self.name
960 }
961}
962
963impl Name for DirectorySourceDist {
964 fn name(&self) -> &PackageName {
965 &self.name
966 }
967}
968
969impl Name for SourceDist {
970 fn name(&self) -> &PackageName {
971 match self {
972 Self::Registry(dist) => dist.name(),
973 Self::DirectUrl(dist) => dist.name(),
974 Self::GitPath(dist) => dist.name(),
975 Self::GitDirectory(dist) => dist.name(),
976 Self::Path(dist) => dist.name(),
977 Self::Directory(dist) => dist.name(),
978 }
979 }
980}
981
982impl Name for BuiltDist {
983 fn name(&self) -> &PackageName {
984 match self {
985 Self::Registry(dist) => dist.name(),
986 Self::DirectUrl(dist) => dist.name(),
987 Self::Path(dist) => dist.name(),
988 Self::GitPath(dist) => dist.name(),
989 }
990 }
991}
992
993impl Name for Dist {
994 fn name(&self) -> &PackageName {
995 match self {
996 Self::Built(dist) => dist.name(),
997 Self::Source(dist) => dist.name(),
998 }
999 }
1000}
1001
1002impl Name for CompatibleDist<'_> {
1003 fn name(&self) -> &PackageName {
1004 match self {
1005 Self::InstalledDist(dist) => dist.name(),
1006 Self::SourceDist {
1007 sdist,
1008 prioritized: _,
1009 } => sdist.name(),
1010 Self::CompatibleWheel {
1011 wheel,
1012 priority: _,
1013 prioritized: _,
1014 } => wheel.name(),
1015 Self::IncompatibleWheel {
1016 sdist,
1017 wheel: _,
1018 prioritized: _,
1019 } => sdist.name(),
1020 }
1021 }
1022}
1023
1024impl DistributionMetadata for RegistryBuiltWheel {
1025 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1026 VersionOrUrlRef::Version(&self.filename.version)
1027 }
1028}
1029
1030impl DistributionMetadata for RegistryBuiltDist {
1031 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1032 self.best_wheel().version_or_url()
1033 }
1034}
1035
1036impl DistributionMetadata for DirectUrlBuiltDist {
1037 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1038 VersionOrUrlRef::Url(&self.url)
1039 }
1040
1041 fn version_id(&self) -> VersionId {
1042 VersionId::from_archive(self.location.as_ref().clone(), None)
1043 }
1044}
1045
1046impl DistributionMetadata for PathBuiltDist {
1047 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1048 VersionOrUrlRef::Url(&self.url)
1049 }
1050
1051 fn version_id(&self) -> VersionId {
1052 VersionId::from_path(self.install_path.as_ref())
1053 }
1054}
1055
1056impl DistributionMetadata for GitPathBuiltDist {
1057 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1058 VersionOrUrlRef::Url(&self.url)
1059 }
1060}
1061
1062impl DistributionMetadata for RegistrySourceDist {
1063 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1064 VersionOrUrlRef::Version(&self.version)
1065 }
1066}
1067
1068impl DistributionMetadata for DirectUrlSourceDist {
1069 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1070 VersionOrUrlRef::Url(&self.url)
1071 }
1072
1073 fn version_id(&self) -> VersionId {
1074 VersionId::from_archive(
1075 self.location.as_ref().clone(),
1076 self.subdirectory.clone().map(Path::into_path_buf),
1077 )
1078 }
1079}
1080
1081impl DistributionMetadata for GitPathSourceDist {
1082 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1083 VersionOrUrlRef::Url(&self.url)
1084 }
1085
1086 fn version_id(&self) -> VersionId {
1087 VersionId::from_git(self.git.as_ref(), Some(&self.install_path))
1088 }
1089}
1090
1091impl DistributionMetadata for GitDirectorySourceDist {
1092 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1093 VersionOrUrlRef::Url(&self.url)
1094 }
1095
1096 fn version_id(&self) -> VersionId {
1097 VersionId::from_git(self.git.as_ref(), self.subdirectory.as_deref())
1098 }
1099}
1100
1101impl DistributionMetadata for PathSourceDist {
1102 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1103 VersionOrUrlRef::Url(&self.url)
1104 }
1105
1106 fn version_id(&self) -> VersionId {
1107 VersionId::from_path(self.install_path.as_ref())
1108 }
1109}
1110
1111impl DistributionMetadata for DirectorySourceDist {
1112 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1113 VersionOrUrlRef::Url(&self.url)
1114 }
1115
1116 fn version_id(&self) -> VersionId {
1117 VersionId::from_directory(self.install_path.as_ref())
1118 }
1119}
1120
1121impl DistributionMetadata for SourceDist {
1122 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1123 match self {
1124 Self::Registry(dist) => dist.version_or_url(),
1125 Self::DirectUrl(dist) => dist.version_or_url(),
1126 Self::GitPath(dist) => dist.version_or_url(),
1127 Self::GitDirectory(dist) => dist.version_or_url(),
1128 Self::Path(dist) => dist.version_or_url(),
1129 Self::Directory(dist) => dist.version_or_url(),
1130 }
1131 }
1132
1133 fn version_id(&self) -> VersionId {
1134 match self {
1135 Self::Registry(dist) => dist.version_id(),
1136 Self::DirectUrl(dist) => dist.version_id(),
1137 Self::GitPath(dist) => dist.version_id(),
1138 Self::GitDirectory(dist) => dist.version_id(),
1139 Self::Path(dist) => dist.version_id(),
1140 Self::Directory(dist) => dist.version_id(),
1141 }
1142 }
1143}
1144
1145impl DistributionMetadata for BuiltDist {
1146 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1147 match self {
1148 Self::Registry(dist) => dist.version_or_url(),
1149 Self::DirectUrl(dist) => dist.version_or_url(),
1150 Self::Path(dist) => dist.version_or_url(),
1151 Self::GitPath(dist) => dist.version_or_url(),
1152 }
1153 }
1154
1155 fn version_id(&self) -> VersionId {
1156 match self {
1157 Self::Registry(dist) => dist.version_id(),
1158 Self::DirectUrl(dist) => dist.version_id(),
1159 Self::Path(dist) => dist.version_id(),
1160 Self::GitPath(dist) => dist.version_id(),
1161 }
1162 }
1163}
1164
1165impl DistributionMetadata for Dist {
1166 fn version_or_url(&self) -> VersionOrUrlRef<'_> {
1167 match self {
1168 Self::Built(dist) => dist.version_or_url(),
1169 Self::Source(dist) => dist.version_or_url(),
1170 }
1171 }
1172
1173 fn version_id(&self) -> VersionId {
1174 match self {
1175 Self::Built(dist) => dist.version_id(),
1176 Self::Source(dist) => dist.version_id(),
1177 }
1178 }
1179}
1180
1181impl RemoteSource for File {
1182 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1183 Ok(Cow::Borrowed(&self.filename))
1184 }
1185
1186 fn size(&self) -> Option<u64> {
1187 self.size
1188 }
1189}
1190
1191impl RemoteSource for Url {
1192 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1193 let mut path_segments = self
1195 .path_segments()
1196 .ok_or_else(|| Error::MissingPathSegments(self.to_string()))?;
1197
1198 let last = path_segments
1200 .next_back()
1201 .expect("path segments is non-empty");
1202
1203 let filename = percent_encoding::percent_decode_str(last).decode_utf8()?;
1205
1206 Ok(filename)
1207 }
1208
1209 fn size(&self) -> Option<u64> {
1210 None
1211 }
1212}
1213
1214impl RemoteSource for UrlString {
1215 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1216 let url = self.as_ref();
1217 if memchr3(b'?', b'#', b'%', url.as_bytes()).is_none()
1218 && let Some((_, filename)) = url.rsplit_once('/')
1219 {
1220 return Ok(Cow::Borrowed(filename));
1221 }
1222
1223 let last = self
1225 .base_str()
1226 .split('/')
1227 .next_back()
1228 .ok_or_else(|| Error::MissingPathSegments(self.to_string()))?;
1229
1230 let filename = percent_encoding::percent_decode_str(last).decode_utf8()?;
1232
1233 Ok(filename)
1234 }
1235
1236 fn size(&self) -> Option<u64> {
1237 None
1238 }
1239}
1240
1241impl RemoteSource for RegistryBuiltWheel {
1242 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1243 self.file.filename()
1244 }
1245
1246 fn size(&self) -> Option<u64> {
1247 self.file.size()
1248 }
1249}
1250
1251impl RemoteSource for RegistryBuiltDist {
1252 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1253 self.best_wheel().filename()
1254 }
1255
1256 fn size(&self) -> Option<u64> {
1257 self.best_wheel().size()
1258 }
1259}
1260
1261impl RemoteSource for RegistrySourceDist {
1262 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1263 self.file.filename()
1264 }
1265
1266 fn size(&self) -> Option<u64> {
1267 self.file.size()
1268 }
1269}
1270
1271impl RemoteSource for DirectUrlBuiltDist {
1272 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1273 self.url.filename()
1274 }
1275
1276 fn size(&self) -> Option<u64> {
1277 self.size
1278 }
1279}
1280
1281impl RemoteSource for DirectUrlSourceDist {
1282 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1283 self.url.filename()
1284 }
1285
1286 fn size(&self) -> Option<u64> {
1287 self.size
1288 }
1289}
1290
1291impl RemoteSource for GitPathSourceDist {
1292 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1293 match self.url.filename()? {
1295 Cow::Borrowed(filename) => {
1296 if let Some((_, filename)) = filename.rsplit_once('@') {
1297 Ok(Cow::Borrowed(filename))
1298 } else {
1299 Ok(Cow::Borrowed(filename))
1300 }
1301 }
1302 Cow::Owned(filename) => {
1303 if let Some((_, filename)) = filename.rsplit_once('@') {
1304 Ok(Cow::Owned(filename.to_owned()))
1305 } else {
1306 Ok(Cow::Owned(filename))
1307 }
1308 }
1309 }
1310 }
1311
1312 fn size(&self) -> Option<u64> {
1313 self.url.size()
1314 }
1315}
1316
1317impl RemoteSource for GitDirectorySourceDist {
1318 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1319 match self.url.filename()? {
1321 Cow::Borrowed(filename) => {
1322 if let Some((_, filename)) = filename.rsplit_once('@') {
1323 Ok(Cow::Borrowed(filename))
1324 } else {
1325 Ok(Cow::Borrowed(filename))
1326 }
1327 }
1328 Cow::Owned(filename) => {
1329 if let Some((_, filename)) = filename.rsplit_once('@') {
1330 Ok(Cow::Owned(filename.to_owned()))
1331 } else {
1332 Ok(Cow::Owned(filename))
1333 }
1334 }
1335 }
1336 }
1337
1338 fn size(&self) -> Option<u64> {
1339 self.url.size()
1340 }
1341}
1342
1343impl RemoteSource for PathBuiltDist {
1344 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1345 self.url.filename()
1346 }
1347
1348 fn size(&self) -> Option<u64> {
1349 self.url.size()
1350 }
1351}
1352
1353impl RemoteSource for GitPathBuiltDist {
1354 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1355 self.url.filename()
1356 }
1357
1358 fn size(&self) -> Option<u64> {
1359 self.url.size()
1360 }
1361}
1362
1363impl RemoteSource for PathSourceDist {
1364 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1365 self.url.filename()
1366 }
1367
1368 fn size(&self) -> Option<u64> {
1369 self.url.size()
1370 }
1371}
1372
1373impl RemoteSource for DirectorySourceDist {
1374 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1375 self.url.filename()
1376 }
1377
1378 fn size(&self) -> Option<u64> {
1379 self.url.size()
1380 }
1381}
1382
1383impl RemoteSource for SourceDist {
1384 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1385 match self {
1386 Self::Registry(dist) => dist.filename(),
1387 Self::DirectUrl(dist) => dist.filename(),
1388 Self::GitPath(dist) => dist.filename(),
1389 Self::GitDirectory(dist) => dist.filename(),
1390 Self::Path(dist) => dist.filename(),
1391 Self::Directory(dist) => dist.filename(),
1392 }
1393 }
1394
1395 fn size(&self) -> Option<u64> {
1396 match self {
1397 Self::Registry(dist) => dist.size(),
1398 Self::DirectUrl(dist) => dist.size(),
1399 Self::GitPath(dist) => dist.size(),
1400 Self::GitDirectory(dist) => dist.size(),
1401 Self::Path(dist) => dist.size(),
1402 Self::Directory(dist) => dist.size(),
1403 }
1404 }
1405}
1406
1407impl RemoteSource for BuiltDist {
1408 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1409 match self {
1410 Self::Registry(dist) => dist.filename(),
1411 Self::DirectUrl(dist) => dist.filename(),
1412 Self::Path(dist) => dist.filename(),
1413 Self::GitPath(dist) => dist.filename(),
1414 }
1415 }
1416
1417 fn size(&self) -> Option<u64> {
1418 match self {
1419 Self::Registry(dist) => dist.size(),
1420 Self::DirectUrl(dist) => dist.size(),
1421 Self::Path(dist) => dist.size(),
1422 Self::GitPath(dist) => dist.size(),
1423 }
1424 }
1425}
1426
1427impl RemoteSource for Dist {
1428 fn filename(&self) -> Result<Cow<'_, str>, Error> {
1429 match self {
1430 Self::Built(dist) => dist.filename(),
1431 Self::Source(dist) => dist.filename(),
1432 }
1433 }
1434
1435 fn size(&self) -> Option<u64> {
1436 match self {
1437 Self::Built(dist) => dist.size(),
1438 Self::Source(dist) => dist.size(),
1439 }
1440 }
1441}
1442
1443impl Identifier for DisplaySafeUrl {
1444 fn distribution_id(&self) -> DistributionId {
1445 DistributionId::Url(uv_cache_key::CanonicalUrl::new(self.clone()))
1446 }
1447
1448 fn resource_id(&self) -> ResourceId {
1449 ResourceId::Url(uv_cache_key::RepositoryUrl::new(self.clone()))
1450 }
1451}
1452
1453impl Identifier for File {
1454 fn distribution_id(&self) -> DistributionId {
1455 self.hashes
1456 .first()
1457 .cloned()
1458 .map(DistributionId::Digest)
1459 .unwrap_or_else(|| self.url.distribution_id())
1460 }
1461
1462 fn resource_id(&self) -> ResourceId {
1463 self.hashes
1464 .first()
1465 .cloned()
1466 .map(ResourceId::Digest)
1467 .unwrap_or_else(|| self.url.resource_id())
1468 }
1469}
1470
1471impl Identifier for Path {
1472 fn distribution_id(&self) -> DistributionId {
1473 DistributionId::PathBuf(self.to_path_buf())
1474 }
1475
1476 fn resource_id(&self) -> ResourceId {
1477 ResourceId::PathBuf(self.to_path_buf())
1478 }
1479}
1480
1481impl Identifier for FileLocation {
1482 fn distribution_id(&self) -> DistributionId {
1483 match self {
1484 Self::RelativeUrl(base, url) => {
1485 DistributionId::RelativeUrl(base.to_string(), url.to_string())
1486 }
1487 Self::AbsoluteUrl(url) => DistributionId::AbsoluteUrl(url.to_string()),
1488 }
1489 }
1490
1491 fn resource_id(&self) -> ResourceId {
1492 match self {
1493 Self::RelativeUrl(base, url) => {
1494 ResourceId::RelativeUrl(base.to_string(), url.to_string())
1495 }
1496 Self::AbsoluteUrl(url) => ResourceId::AbsoluteUrl(url.to_string()),
1497 }
1498 }
1499}
1500
1501impl Identifier for RegistryBuiltWheel {
1502 fn distribution_id(&self) -> DistributionId {
1503 self.file.distribution_id()
1504 }
1505
1506 fn resource_id(&self) -> ResourceId {
1507 self.file.resource_id()
1508 }
1509}
1510
1511impl Identifier for RegistryBuiltDist {
1512 fn distribution_id(&self) -> DistributionId {
1513 self.best_wheel().distribution_id()
1514 }
1515
1516 fn resource_id(&self) -> ResourceId {
1517 self.best_wheel().resource_id()
1518 }
1519}
1520
1521impl Identifier for RegistrySourceDist {
1522 fn distribution_id(&self) -> DistributionId {
1523 self.file.distribution_id()
1524 }
1525
1526 fn resource_id(&self) -> ResourceId {
1527 self.file.resource_id()
1528 }
1529}
1530
1531impl Identifier for DirectUrlBuiltDist {
1532 fn distribution_id(&self) -> DistributionId {
1533 self.url.distribution_id()
1534 }
1535
1536 fn resource_id(&self) -> ResourceId {
1537 self.url.resource_id()
1538 }
1539}
1540
1541impl Identifier for DirectUrlSourceDist {
1542 fn distribution_id(&self) -> DistributionId {
1543 self.url.distribution_id()
1544 }
1545
1546 fn resource_id(&self) -> ResourceId {
1547 self.url.resource_id()
1548 }
1549}
1550
1551impl Identifier for PathBuiltDist {
1552 fn distribution_id(&self) -> DistributionId {
1553 self.url.distribution_id()
1554 }
1555
1556 fn resource_id(&self) -> ResourceId {
1557 self.url.resource_id()
1558 }
1559}
1560
1561impl Identifier for GitPathBuiltDist {
1562 fn distribution_id(&self) -> DistributionId {
1563 self.url.distribution_id()
1564 }
1565
1566 fn resource_id(&self) -> ResourceId {
1567 self.url.resource_id()
1568 }
1569}
1570
1571impl Identifier for PathSourceDist {
1572 fn distribution_id(&self) -> DistributionId {
1573 self.url.distribution_id()
1574 }
1575
1576 fn resource_id(&self) -> ResourceId {
1577 self.url.resource_id()
1578 }
1579}
1580
1581impl Identifier for DirectorySourceDist {
1582 fn distribution_id(&self) -> DistributionId {
1583 self.url.distribution_id()
1584 }
1585
1586 fn resource_id(&self) -> ResourceId {
1587 self.url.resource_id()
1588 }
1589}
1590
1591impl Identifier for GitPathSourceDist {
1592 fn distribution_id(&self) -> DistributionId {
1593 self.url.distribution_id()
1594 }
1595
1596 fn resource_id(&self) -> ResourceId {
1597 self.url.resource_id()
1598 }
1599}
1600
1601impl Identifier for GitDirectorySourceDist {
1602 fn distribution_id(&self) -> DistributionId {
1603 self.url.distribution_id()
1604 }
1605
1606 fn resource_id(&self) -> ResourceId {
1607 self.url.resource_id()
1608 }
1609}
1610
1611impl Identifier for SourceDist {
1612 fn distribution_id(&self) -> DistributionId {
1613 match self {
1614 Self::Registry(dist) => dist.distribution_id(),
1615 Self::DirectUrl(dist) => dist.distribution_id(),
1616 Self::GitPath(dist) => dist.distribution_id(),
1617 Self::GitDirectory(dist) => dist.distribution_id(),
1618 Self::Path(dist) => dist.distribution_id(),
1619 Self::Directory(dist) => dist.distribution_id(),
1620 }
1621 }
1622
1623 fn resource_id(&self) -> ResourceId {
1624 match self {
1625 Self::Registry(dist) => dist.resource_id(),
1626 Self::DirectUrl(dist) => dist.resource_id(),
1627 Self::GitPath(dist) => dist.resource_id(),
1628 Self::GitDirectory(dist) => dist.resource_id(),
1629 Self::Path(dist) => dist.resource_id(),
1630 Self::Directory(dist) => dist.resource_id(),
1631 }
1632 }
1633}
1634
1635impl Identifier for BuiltDist {
1636 fn distribution_id(&self) -> DistributionId {
1637 match self {
1638 Self::Registry(dist) => dist.distribution_id(),
1639 Self::DirectUrl(dist) => dist.distribution_id(),
1640 Self::Path(dist) => dist.distribution_id(),
1641 Self::GitPath(dist) => dist.distribution_id(),
1642 }
1643 }
1644
1645 fn resource_id(&self) -> ResourceId {
1646 match self {
1647 Self::Registry(dist) => dist.resource_id(),
1648 Self::DirectUrl(dist) => dist.resource_id(),
1649 Self::Path(dist) => dist.resource_id(),
1650 Self::GitPath(dist) => dist.resource_id(),
1651 }
1652 }
1653}
1654
1655impl Identifier for InstalledDist {
1656 fn distribution_id(&self) -> DistributionId {
1657 self.install_path().distribution_id()
1658 }
1659
1660 fn resource_id(&self) -> ResourceId {
1661 self.install_path().resource_id()
1662 }
1663}
1664
1665impl Identifier for Dist {
1666 fn distribution_id(&self) -> DistributionId {
1667 match self {
1668 Self::Built(dist) => dist.distribution_id(),
1669 Self::Source(dist) => dist.distribution_id(),
1670 }
1671 }
1672
1673 fn resource_id(&self) -> ResourceId {
1674 match self {
1675 Self::Built(dist) => dist.resource_id(),
1676 Self::Source(dist) => dist.resource_id(),
1677 }
1678 }
1679}
1680
1681impl Identifier for DirectSourceUrl<'_> {
1682 fn distribution_id(&self) -> DistributionId {
1683 self.url.distribution_id()
1684 }
1685
1686 fn resource_id(&self) -> ResourceId {
1687 self.url.resource_id()
1688 }
1689}
1690
1691impl Identifier for GitDirectorySourceUrl<'_> {
1692 fn distribution_id(&self) -> DistributionId {
1693 self.url.distribution_id()
1694 }
1695
1696 fn resource_id(&self) -> ResourceId {
1697 self.url.resource_id()
1698 }
1699}
1700
1701impl Identifier for GitPathSourceUrl<'_> {
1702 fn distribution_id(&self) -> DistributionId {
1703 self.url.distribution_id()
1704 }
1705
1706 fn resource_id(&self) -> ResourceId {
1707 self.url.resource_id()
1708 }
1709}
1710
1711impl Identifier for PathSourceUrl<'_> {
1712 fn distribution_id(&self) -> DistributionId {
1713 self.url.distribution_id()
1714 }
1715
1716 fn resource_id(&self) -> ResourceId {
1717 self.url.resource_id()
1718 }
1719}
1720
1721impl Identifier for DirectorySourceUrl<'_> {
1722 fn distribution_id(&self) -> DistributionId {
1723 self.url.distribution_id()
1724 }
1725
1726 fn resource_id(&self) -> ResourceId {
1727 self.url.resource_id()
1728 }
1729}
1730
1731impl Identifier for SourceUrl<'_> {
1732 fn distribution_id(&self) -> DistributionId {
1733 match self {
1734 Self::Direct(url) => url.distribution_id(),
1735 Self::GitDirectory(url) => url.distribution_id(),
1736 Self::GitPath(url) => url.distribution_id(),
1737 Self::Path(url) => url.distribution_id(),
1738 Self::Directory(url) => url.distribution_id(),
1739 }
1740 }
1741
1742 fn resource_id(&self) -> ResourceId {
1743 match self {
1744 Self::Direct(url) => url.resource_id(),
1745 Self::GitDirectory(url) => url.resource_id(),
1746 Self::GitPath(url) => url.resource_id(),
1747 Self::Path(url) => url.resource_id(),
1748 Self::Directory(url) => url.resource_id(),
1749 }
1750 }
1751}
1752
1753impl Identifier for BuildableSource<'_> {
1754 fn distribution_id(&self) -> DistributionId {
1755 match self {
1756 Self::Dist(source) => source.distribution_id(),
1757 Self::Url(source) => source.distribution_id(),
1758 }
1759 }
1760
1761 fn resource_id(&self) -> ResourceId {
1762 match self {
1763 Self::Dist(source) => source.resource_id(),
1764 Self::Url(source) => source.resource_id(),
1765 }
1766 }
1767}
1768
1769#[cfg(test)]
1770mod test {
1771 use crate::{BuiltDist, Dist, RemoteSource, SourceDist, UrlString};
1772 use uv_redacted::DisplaySafeUrl;
1773
1774 #[test]
1776 fn dist_size() {
1777 assert!(size_of::<Dist>() <= 200, "{}", size_of::<Dist>());
1778 assert!(size_of::<BuiltDist>() <= 200, "{}", size_of::<BuiltDist>());
1779 assert!(
1780 size_of::<SourceDist>() <= 176,
1781 "{}",
1782 size_of::<SourceDist>()
1783 );
1784 }
1785
1786 #[test]
1787 fn remote_source() {
1788 for url in [
1789 "https://example.com/foo-0.1.0.tar.gz",
1790 "https://example.com/foo%2D0.1.0.tar.gz",
1791 "https://example.com/foo-0.1.0.tar.gz#fragment",
1792 "https://example.com/foo-0.1.0.tar.gz?query",
1793 "https://example.com/foo-0.1.0.tar.gz?query#fragment",
1794 "https://example.com/foo-0.1.0.tar.gz?query=1/2#fragment",
1795 "https://example.com/foo-0.1.0.tar.gz?query=1/2#fragment/3",
1796 "https://example.com/foo%2D0.1.0.tar.gz?query=1/2#fragment/3",
1797 ] {
1798 let url = DisplaySafeUrl::parse(url).unwrap();
1799 assert_eq!(url.filename().unwrap(), "foo-0.1.0.tar.gz", "{url}");
1800 let url = UrlString::from(url.clone());
1801 assert_eq!(url.filename().unwrap(), "foo-0.1.0.tar.gz", "{url}");
1802 }
1803 }
1804}