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