diff --git a/crates/distribution-types/src/editable.rs b/crates/distribution-types/src/editable.rs index b0c1ae8bff4c..d4901a325519 100644 --- a/crates/distribution-types/src/editable.rs +++ b/crates/distribution-types/src/editable.rs @@ -1,7 +1,8 @@ -use requirements_txt::EditableRequirement; use std::fmt::{Display, Formatter}; -use std::path::PathBuf; -use url::Url; +use std::path::{Path, PathBuf}; + +use pep508_rs::VerbatimUrl; +use requirements_txt::EditableRequirement; #[derive(Debug, Clone)] pub struct LocalEditable { @@ -11,8 +12,12 @@ pub struct LocalEditable { } impl LocalEditable { - pub fn url(&self) -> Url { - Url::from_directory_path(&self.path).expect("A valid path makes a valid url") + pub fn url(&self) -> VerbatimUrl { + self.requirement.url() + } + + pub fn path(&self) -> &Path { + &self.path } } diff --git a/crates/pep508-rs/src/verbatim_url.rs b/crates/pep508-rs/src/verbatim_url.rs index 2a7300dc63c8..c6da1cad265e 100644 --- a/crates/pep508-rs/src/verbatim_url.rs +++ b/crates/pep508-rs/src/verbatim_url.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::ops::Deref; +use std::path::Path; use once_cell::sync::Lazy; use regex::Regex; @@ -27,6 +28,14 @@ impl VerbatimUrl { }) } + /// Helper method `LocalEditable` + pub fn from_path(path: &Path, given: String) -> Result { + Ok(Self { + url: Url::from_directory_path(path)?, + given: Some(given), + }) + } + /// Return the underlying [`Url`]. pub fn raw(&self) -> &Url { &self.url diff --git a/crates/puffin-distribution/src/source_dist.rs b/crates/puffin-distribution/src/source_dist.rs index 1e24da7caff6..1a993738175d 100644 --- a/crates/puffin-distribution/src/source_dist.rs +++ b/crates/puffin-distribution/src/source_dist.rs @@ -25,7 +25,6 @@ use distribution_types::{ Dist, GitSourceDist, LocalEditable, Metadata, PathSourceDist, RemoteSource, SourceDist, }; use install_wheel_rs::read_dist_info; -use pep508_rs::VerbatimUrl; use platform_tags::Tags; use puffin_cache::{ digest, CacheBucket, CacheEntry, CacheShard, CachedByTimestamp, CanonicalUrl, WheelCache, @@ -688,7 +687,7 @@ impl<'a, T: BuildContext> SourceDistCachedBuilder<'a, T> { // We finally have the name of the package and can construct the dist let dist = Dist::Source(SourceDist::Path(PathSourceDist { name: filename.name.clone(), - url: VerbatimUrl::unknown(editable.url()), + url: editable.url(), path: editable.path.clone(), editable: true, })); diff --git a/crates/puffin-installer/src/plan.rs b/crates/puffin-installer/src/plan.rs index 7dab6fb64131..f3e9b70f706c 100644 --- a/crates/puffin-installer/src/plan.rs +++ b/crates/puffin-installer/src/plan.rs @@ -73,7 +73,7 @@ impl InstallPlan { for editable in editable_requirements { let editable_dist = site_packages .editables() - .find(|(_dist, url, _dir_info)| *url == &editable.url()) + .find(|(_dist, url, _dir_info)| url == &editable.url().raw()) .map(|(dist, _url, _dir_info)| dist.clone()); if let Some(dist) = editable_dist { debug!("Treating editable requirement as immutable: {editable}"); diff --git a/crates/puffin-resolver/src/resolver.rs b/crates/puffin-resolver/src/resolver.rs index f8b309939698..e4322f261447 100644 --- a/crates/puffin-resolver/src/resolver.rs +++ b/crates/puffin-resolver/src/resolver.rs @@ -22,7 +22,7 @@ use distribution_types::{ BuiltDist, Dist, DistributionId, Identifier, LocalEditable, Metadata, PackageId, SourceDist, VersionOrUrl, }; -use pep508_rs::{MarkerEnvironment, Requirement, VerbatimUrl}; +use pep508_rs::{MarkerEnvironment, Requirement}; use platform_tags::Tags; use puffin_cache::CanonicalUrl; use puffin_client::RegistryClient; @@ -202,11 +202,8 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { let index = Index::default(); let mut editables = FxHashMap::default(); for (editable_requirement, metadata) in &manifest.editables { - let dist = Dist::from_url( - metadata.name.clone(), - VerbatimUrl::unknown(editable_requirement.url()), - ) - .expect("This is a valid distribution"); + let dist = Dist::from_url(metadata.name.clone(), editable_requirement.url()) + .expect("This is a valid distribution"); // Mock editable responses index.distributions.register(&dist.package_id()); index @@ -237,7 +234,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { manifest .editables .iter() - .map(|(editable, _)| editable.url()), + .map(|(editable, _)| editable.url().raw().clone()), ) .collect(), project: manifest.project, @@ -638,11 +635,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> { for (editable, metadata) in self.editables.values() { constraints.insert( - PubGrubPackage::Package( - metadata.name.clone(), - None, - Some(VerbatimUrl::unknown(editable.url())), - ), + PubGrubPackage::Package(metadata.name.clone(), None, Some(editable.url())), Range::singleton(PubGrubVersion::from(metadata.version.clone())), ); } diff --git a/crates/requirements-txt/src/lib.rs b/crates/requirements-txt/src/lib.rs index 7c51d0fa8f17..abe7e4aa8e71 100644 --- a/crates/requirements-txt/src/lib.rs +++ b/crates/requirements-txt/src/lib.rs @@ -44,9 +44,8 @@ use fs_err as fs; use serde::{Deserialize, Serialize}; use tracing::warn; use unscanny::{Pattern, Scanner}; -use url::Url; -use pep508_rs::{Pep508Error, Requirement}; +use pep508_rs::{Pep508Error, Requirement, VerbatimUrl}; /// We emit one of those for each requirements.txt entry enum RequirementsTxtStatement { @@ -68,61 +67,53 @@ enum RequirementsTxtStatement { EditableRequirement(ParsedEditableRequirement), } -#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Serialize)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum EditableRequirement { Path { /// The absolute path, resolved from the current dir if the path in the requirements file was relative resolved: PathBuf, /// The relative path as it is written in the requirements file - original: PathBuf, + original: String, }, - Url(Url), + Url(VerbatimUrl), } impl EditableRequirement { - pub fn url(&self) -> Url { + pub fn url(&self) -> VerbatimUrl { match self { - EditableRequirement::Path { - resolved, - original: _, - } => Url::from_directory_path(resolved).expect("Path valid for url"), + EditableRequirement::Path { resolved, original } => { + VerbatimUrl::from_path(resolved, original.clone()) + .expect("A valid path makes a valid url") + } EditableRequirement::Url(url) => url.clone(), } } } /// Relative paths aren't resolved with the current dir yet -#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Serialize)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum ParsedEditableRequirement { - Path(PathBuf), - Url(Url), + Path(String), + Url(VerbatimUrl), } impl ParsedEditableRequirement { - pub fn unwrap_path(&self) -> &Path { - match self { - ParsedEditableRequirement::Path(path) => path, - ParsedEditableRequirement::Url(_) => { - todo!("url editables") - } - } - } - pub fn with_working_dir( self, working_dir: impl AsRef, ) -> io::Result { Ok(match self { ParsedEditableRequirement::Path(path) => { - if path.is_absolute() { + let path_buf = PathBuf::from(&path); + if path_buf.is_absolute() { EditableRequirement::Path { - resolved: path.clone(), + resolved: path_buf, original: path, } } else { EditableRequirement::Path { // Avoid paths like `/home/ferris/project/scripts/../editable/` - resolved: fs::canonicalize(working_dir.as_ref().join(&path))?, + resolved: fs::canonicalize(working_dir.as_ref().join(&path_buf))?, original: path, } } @@ -136,7 +127,7 @@ impl ParsedEditableRequirement { impl Display for ParsedEditableRequirement { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - ParsedEditableRequirement::Path(path) => path.display().fmt(f), + ParsedEditableRequirement::Path(path) => path.fmt(f), ParsedEditableRequirement::Url(url) => url.fmt(f), } } @@ -148,7 +139,7 @@ impl Display for EditableRequirement { EditableRequirement::Path { original, resolved: _, - } => original.display().fmt(f), + } => original.fmt(f), EditableRequirement::Url(url) => url.fmt(f), } } @@ -180,7 +171,7 @@ impl Display for RequirementEntry { } /// Parsed and flattened requirements.txt with requirements and constraints -#[derive(Debug, Deserialize, Clone, Default, Eq, PartialEq, Serialize)] +#[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct RequirementsTxt { /// The actual requirements with the hashes pub requirements: Vec, @@ -326,10 +317,10 @@ fn parse_entry( } } else if s.eat_if("-e") { let path_or_url = parse_value(s, |c: char| !['\n', '\r'].contains(&c))?; - let editable_requirement = if let Ok(url) = Url::from_str(path_or_url) { + let editable_requirement = if let Ok(url) = VerbatimUrl::from_str(path_or_url) { ParsedEditableRequirement::Url(url) } else { - ParsedEditableRequirement::Path(PathBuf::from(path_or_url)) + ParsedEditableRequirement::Path(path_or_url.to_string()) }; RequirementsTxtStatement::EditableRequirement(editable_requirement) } else if s.at(char::is_ascii_alphanumeric) {