mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-16 18:45:41 +00:00
Capture portable path serialization in a struct (#5652)
## Summary I need to reuse this in #5494, so want to abstract it out and make it reusable.
This commit is contained in:
parent
8d14a4cb4f
commit
dfec262586
9 changed files with 169 additions and 79 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -4826,6 +4826,7 @@ dependencies = [
|
||||||
"junction",
|
"junction",
|
||||||
"path-absolutize",
|
"path-absolutize",
|
||||||
"path-slash",
|
"path-slash",
|
||||||
|
"serde",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tracing",
|
"tracing",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
|
|
@ -5016,7 +5017,6 @@ dependencies = [
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"once-map",
|
"once-map",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"path-slash",
|
|
||||||
"pep440_rs",
|
"pep440_rs",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
|
|
@ -5040,6 +5040,7 @@ dependencies = [
|
||||||
"uv-client",
|
"uv-client",
|
||||||
"uv-configuration",
|
"uv-configuration",
|
||||||
"uv-distribution",
|
"uv-distribution",
|
||||||
|
"uv-fs",
|
||||||
"uv-git",
|
"uv-git",
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-python",
|
"uv-python",
|
||||||
|
|
@ -5117,7 +5118,6 @@ dependencies = [
|
||||||
"dirs-sys",
|
"dirs-sys",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"install-wheel-rs",
|
"install-wheel-rs",
|
||||||
"path-slash",
|
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"pep440_rs",
|
"pep440_rs",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
|
|
@ -5192,7 +5192,6 @@ dependencies = [
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"glob",
|
"glob",
|
||||||
"insta",
|
"insta",
|
||||||
"path-slash",
|
|
||||||
"pep440_rs",
|
"pep440_rs",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
"pypi-types",
|
"pypi-types",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ fs-err = { workspace = true }
|
||||||
fs2 = { workspace = true }
|
fs2 = { workspace = true }
|
||||||
path-absolutize = { workspace = true }
|
path-absolutize = { workspace = true }
|
||||||
path-slash = { workspace = true }
|
path-slash = { workspace = true }
|
||||||
|
serde = { workspace = true, optional = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
urlencoding = { workspace = true }
|
urlencoding = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,100 @@ pub fn relative_to(
|
||||||
Ok(up.join(stripped))
|
Ok(up.join(stripped))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A path that can be serialized and deserialized in a portable way by converting Windows-style
|
||||||
|
/// backslashes to forward slashes, and using a `.` for an empty path.
|
||||||
|
///
|
||||||
|
/// This implementation assumes that the path is valid UTF-8; otherwise, it won't roundtrip.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct PortablePath<'a>(&'a Path);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct PortablePathBuf(PathBuf);
|
||||||
|
|
||||||
|
impl AsRef<Path> for PortablePath<'_> {
|
||||||
|
fn as_ref(&self) -> &Path {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> From<&'a T> for PortablePath<'a>
|
||||||
|
where
|
||||||
|
T: AsRef<Path> + ?Sized,
|
||||||
|
{
|
||||||
|
fn from(path: &'a T) -> Self {
|
||||||
|
PortablePath(path.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for PortablePath<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let path = self.0.to_slash_lossy();
|
||||||
|
if path.is_empty() {
|
||||||
|
write!(f, ".")
|
||||||
|
} else {
|
||||||
|
write!(f, "{path}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for PortablePathBuf {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let path = self.0.to_slash_lossy();
|
||||||
|
if path.is_empty() {
|
||||||
|
write!(f, ".")
|
||||||
|
} else {
|
||||||
|
write!(f, "{path}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PortablePathBuf> for PathBuf {
|
||||||
|
fn from(portable: PortablePathBuf) -> Self {
|
||||||
|
portable.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PathBuf> for PortablePathBuf {
|
||||||
|
fn from(path: PathBuf) -> Self {
|
||||||
|
Self(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for PortablePathBuf {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::ser::Serializer,
|
||||||
|
{
|
||||||
|
self.to_string().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for PortablePath<'_> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::ser::Serializer,
|
||||||
|
{
|
||||||
|
self.to_string().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> serde::de::Deserialize<'de> for PortablePathBuf {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
if s == "." {
|
||||||
|
Ok(Self(PathBuf::new()))
|
||||||
|
} else {
|
||||||
|
Ok(Self(PathBuf::from(s)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,10 @@ requirements-txt = { workspace = true }
|
||||||
uv-client = { workspace = true }
|
uv-client = { workspace = true }
|
||||||
uv-configuration = { workspace = true }
|
uv-configuration = { workspace = true }
|
||||||
uv-distribution = { workspace = true }
|
uv-distribution = { workspace = true }
|
||||||
|
uv-fs = { workspace = true, features = ["serde"] }
|
||||||
uv-git = { workspace = true }
|
uv-git = { workspace = true }
|
||||||
uv-python = { workspace = true }
|
|
||||||
uv-normalize = { workspace = true }
|
uv-normalize = { workspace = true }
|
||||||
|
uv-python = { workspace = true }
|
||||||
uv-types = { workspace = true }
|
uv-types = { workspace = true }
|
||||||
uv-warnings = { workspace = true }
|
uv-warnings = { workspace = true }
|
||||||
uv-workspace = { workspace = true }
|
uv-workspace = { workspace = true }
|
||||||
|
|
@ -43,7 +44,6 @@ futures = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
owo-colors = { workspace = true }
|
owo-colors = { workspace = true }
|
||||||
path-slash = { workspace = true }
|
|
||||||
petgraph = { workspace = true }
|
petgraph = { workspace = true }
|
||||||
pubgrub = { workspace = true }
|
pubgrub = { workspace = true }
|
||||||
rkyv = { workspace = true }
|
rkyv = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||||
|
use std::convert::Infallible;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
@ -9,10 +10,8 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use path_slash::PathExt;
|
|
||||||
use petgraph::visit::EdgeRef;
|
use petgraph::visit::EdgeRef;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use serde::{Deserialize, Deserializer};
|
|
||||||
use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
|
use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
@ -35,6 +34,7 @@ use pypi_types::{
|
||||||
};
|
};
|
||||||
use uv_configuration::{ExtrasSpecification, Upgrade};
|
use uv_configuration::{ExtrasSpecification, Upgrade};
|
||||||
use uv_distribution::{ArchiveMetadata, Metadata};
|
use uv_distribution::{ArchiveMetadata, Metadata};
|
||||||
|
use uv_fs::{PortablePath, PortablePathBuf};
|
||||||
use uv_git::{GitReference, GitSha, RepositoryReference, ResolvedRepositoryReference};
|
use uv_git::{GitReference, GitSha, RepositoryReference, ResolvedRepositoryReference};
|
||||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||||
use uv_workspace::VirtualProject;
|
use uv_workspace::VirtualProject;
|
||||||
|
|
@ -1370,19 +1370,6 @@ enum Source {
|
||||||
Editable(PathBuf),
|
Editable(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`PathBuf`], but we show `.` instead of an empty path.
|
|
||||||
///
|
|
||||||
/// We also normalize backslashes to forward slashes on Windows, to ensure
|
|
||||||
/// that the lockfile contains portable paths.
|
|
||||||
fn serialize_path_with_dot(path: &Path) -> Cow<str> {
|
|
||||||
let path = path.to_slash_lossy();
|
|
||||||
if path.is_empty() {
|
|
||||||
Cow::Borrowed(".")
|
|
||||||
} else {
|
|
||||||
path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
fn from_resolved_dist(resolved_dist: &ResolvedDist) -> Source {
|
fn from_resolved_dist(resolved_dist: &ResolvedDist) -> Source {
|
||||||
match *resolved_dist {
|
match *resolved_dist {
|
||||||
|
|
@ -1514,21 +1501,18 @@ impl Source {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Source::Path(ref path) => {
|
Source::Path(ref path) => {
|
||||||
source_table.insert(
|
source_table.insert("path", Value::from(PortablePath::from(path).to_string()));
|
||||||
"path",
|
|
||||||
Value::from(serialize_path_with_dot(path).into_owned()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Source::Directory(ref path) => {
|
Source::Directory(ref path) => {
|
||||||
source_table.insert(
|
source_table.insert(
|
||||||
"directory",
|
"directory",
|
||||||
Value::from(serialize_path_with_dot(path).into_owned()),
|
Value::from(PortablePath::from(path).to_string()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Source::Editable(ref path) => {
|
Source::Editable(ref path) => {
|
||||||
source_table.insert(
|
source_table.insert(
|
||||||
"editable",
|
"editable",
|
||||||
Value::from(serialize_path_with_dot(path).into_owned()),
|
Value::from(PortablePath::from(path).to_string()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1543,7 +1527,7 @@ impl std::fmt::Display for Source {
|
||||||
write!(f, "{}+{}", self.name(), url)
|
write!(f, "{}+{}", self.name(), url)
|
||||||
}
|
}
|
||||||
Source::Path(path) | Source::Directory(path) | Source::Editable(path) => {
|
Source::Path(path) | Source::Directory(path) | Source::Editable(path) => {
|
||||||
write!(f, "{}+{}", self.name(), serialize_path_with_dot(path))
|
write!(f, "{}+{}", self.name(), PortablePath::from(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1592,16 +1576,13 @@ enum SourceWire {
|
||||||
subdirectory: Option<String>,
|
subdirectory: Option<String>,
|
||||||
},
|
},
|
||||||
Path {
|
Path {
|
||||||
#[serde(deserialize_with = "deserialize_path_with_dot")]
|
path: PortablePathBuf,
|
||||||
path: PathBuf,
|
|
||||||
},
|
},
|
||||||
Directory {
|
Directory {
|
||||||
#[serde(deserialize_with = "deserialize_path_with_dot")]
|
directory: PortablePathBuf,
|
||||||
directory: PathBuf,
|
|
||||||
},
|
},
|
||||||
Editable {
|
Editable {
|
||||||
#[serde(deserialize_with = "deserialize_path_with_dot")]
|
editable: PortablePathBuf,
|
||||||
editable: PathBuf,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1634,9 +1615,9 @@ impl TryFrom<SourceWire> for Source {
|
||||||
Ok(Source::Git(url, git_source))
|
Ok(Source::Git(url, git_source))
|
||||||
}
|
}
|
||||||
Direct { url, subdirectory } => Ok(Source::Direct(url, DirectSource { subdirectory })),
|
Direct { url, subdirectory } => Ok(Source::Direct(url, DirectSource { subdirectory })),
|
||||||
Path { path } => Ok(Source::Path(path)),
|
Path { path } => Ok(Source::Path(path.into())),
|
||||||
Directory { directory } => Ok(Source::Directory(directory)),
|
Directory { directory } => Ok(Source::Directory(directory.into())),
|
||||||
Editable { editable } => Ok(Source::Editable(editable)),
|
Editable { editable } => Ok(Source::Editable(editable.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1719,7 +1700,7 @@ struct SourceDistMetadata {
|
||||||
/// future, so this should be treated as only a hint to where to look
|
/// future, so this should be treated as only a hint to where to look
|
||||||
/// and/or recording where the source dist file originally came from.
|
/// and/or recording where the source dist file originally came from.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, serde::Deserialize, PartialEq, Eq)]
|
||||||
#[serde(untagged)]
|
#[serde(try_from = "SourceDistWire")]
|
||||||
enum SourceDist {
|
enum SourceDist {
|
||||||
Url {
|
Url {
|
||||||
url: UrlString,
|
url: UrlString,
|
||||||
|
|
@ -1727,26 +1708,12 @@ enum SourceDist {
|
||||||
metadata: SourceDistMetadata,
|
metadata: SourceDistMetadata,
|
||||||
},
|
},
|
||||||
Path {
|
Path {
|
||||||
#[serde(deserialize_with = "deserialize_path_with_dot")]
|
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
metadata: SourceDistMetadata,
|
metadata: SourceDistMetadata,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [`PathBuf`], but we show `.` instead of an empty path.
|
|
||||||
fn deserialize_path_with_dot<'de, D>(deserializer: D) -> Result<PathBuf, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let path = String::deserialize(deserializer)?;
|
|
||||||
if path == "." {
|
|
||||||
Ok(PathBuf::new())
|
|
||||||
} else {
|
|
||||||
Ok(PathBuf::from(path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SourceDist {
|
impl SourceDist {
|
||||||
fn filename(&self) -> Option<Cow<str>> {
|
fn filename(&self) -> Option<Cow<str>> {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -1780,26 +1747,6 @@ impl SourceDist {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceDist {
|
impl SourceDist {
|
||||||
/// Returns the TOML representation of this source distribution.
|
|
||||||
fn to_toml(&self) -> anyhow::Result<InlineTable> {
|
|
||||||
let mut table = InlineTable::new();
|
|
||||||
match &self {
|
|
||||||
SourceDist::Url { url, .. } => {
|
|
||||||
table.insert("url", Value::from(url.as_ref()));
|
|
||||||
}
|
|
||||||
SourceDist::Path { path, .. } => {
|
|
||||||
table.insert("path", Value::from(serialize_path_with_dot(path).as_ref()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(hash) = self.hash() {
|
|
||||||
table.insert("hash", Value::from(hash.to_string()));
|
|
||||||
}
|
|
||||||
if let Some(size) = self.size() {
|
|
||||||
table.insert("size", Value::from(i64::try_from(size)?));
|
|
||||||
}
|
|
||||||
Ok(table)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_annotated_dist(
|
fn from_annotated_dist(
|
||||||
id: &DistributionId,
|
id: &DistributionId,
|
||||||
annotated_dist: &AnnotatedDist,
|
annotated_dist: &AnnotatedDist,
|
||||||
|
|
@ -1890,6 +1837,57 @@ impl SourceDist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum SourceDistWire {
|
||||||
|
Url {
|
||||||
|
url: UrlString,
|
||||||
|
#[serde(flatten)]
|
||||||
|
metadata: SourceDistMetadata,
|
||||||
|
},
|
||||||
|
Path {
|
||||||
|
path: PortablePathBuf,
|
||||||
|
#[serde(flatten)]
|
||||||
|
metadata: SourceDistMetadata,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceDist {
|
||||||
|
/// Returns the TOML representation of this source distribution.
|
||||||
|
fn to_toml(&self) -> anyhow::Result<InlineTable> {
|
||||||
|
let mut table = InlineTable::new();
|
||||||
|
match &self {
|
||||||
|
SourceDist::Url { url, .. } => {
|
||||||
|
table.insert("url", Value::from(url.as_ref()));
|
||||||
|
}
|
||||||
|
SourceDist::Path { path, .. } => {
|
||||||
|
table.insert("path", Value::from(PortablePath::from(path).to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(hash) = self.hash() {
|
||||||
|
table.insert("hash", Value::from(hash.to_string()));
|
||||||
|
}
|
||||||
|
if let Some(size) = self.size() {
|
||||||
|
table.insert("size", Value::from(i64::try_from(size)?));
|
||||||
|
}
|
||||||
|
Ok(table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SourceDistWire> for SourceDist {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
fn try_from(wire: SourceDistWire) -> Result<SourceDist, Infallible> {
|
||||||
|
match wire {
|
||||||
|
SourceDistWire::Url { url, metadata } => Ok(SourceDist::Url { url, metadata }),
|
||||||
|
SourceDistWire::Path { path, metadata } => Ok(SourceDist::Path {
|
||||||
|
path: path.into(),
|
||||||
|
metadata,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<GitReference> for GitSourceKind {
|
impl From<GitReference> for GitSourceKind {
|
||||||
fn from(value: GitReference) -> Self {
|
fn from(value: GitReference) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ uv-installer = { workspace = true }
|
||||||
|
|
||||||
dirs-sys = { workspace = true }
|
dirs-sys = { workspace = true }
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
path-slash = { workspace = true }
|
|
||||||
pathdiff = { workspace = true }
|
pathdiff = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use path_slash::PathBufExt;
|
|
||||||
use pypi_types::VerbatimParsedUrl;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use toml_edit::value;
|
use toml_edit::value;
|
||||||
use toml_edit::Array;
|
use toml_edit::Array;
|
||||||
use toml_edit::Table;
|
use toml_edit::Table;
|
||||||
use toml_edit::Value;
|
use toml_edit::Value;
|
||||||
|
|
||||||
|
use pypi_types::VerbatimParsedUrl;
|
||||||
|
use uv_fs::PortablePath;
|
||||||
|
|
||||||
/// A tool entry.
|
/// A tool entry.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||||
|
|
@ -127,7 +128,7 @@ impl ToolEntrypoint {
|
||||||
table.insert(
|
table.insert(
|
||||||
"install-path",
|
"install-path",
|
||||||
// Use cross-platform slashes so the toml string type does not change
|
// Use cross-platform slashes so the toml string type does not change
|
||||||
value(self.install_path.to_slash_lossy().to_string()),
|
value(PortablePath::from(&self.install_path).to_string()),
|
||||||
);
|
);
|
||||||
table
|
table
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ uv-options-metadata = { workspace = true }
|
||||||
either = { workspace = true }
|
either = { workspace = true }
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
glob = { workspace = true }
|
glob = { workspace = true }
|
||||||
path-slash = { workspace = true }
|
|
||||||
rustc-hash = { workspace = true }
|
rustc-hash = { workspace = true }
|
||||||
schemars = { workspace = true, optional = true }
|
schemars = { workspace = true, optional = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{fmt, mem};
|
use std::{fmt, mem};
|
||||||
|
|
||||||
use path_slash::PathExt;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use toml_edit::{Array, DocumentMut, Item, RawString, Table, TomlError, Value};
|
use toml_edit::{Array, DocumentMut, Item, RawString, Table, TomlError, Value};
|
||||||
|
|
||||||
use pep508_rs::{ExtraName, PackageName, Requirement, VersionOrUrl};
|
use pep508_rs::{ExtraName, PackageName, Requirement, VersionOrUrl};
|
||||||
|
use uv_fs::PortablePath;
|
||||||
|
|
||||||
use crate::pyproject::{DependencyType, PyProjectToml, Source};
|
use crate::pyproject::{DependencyType, PyProjectToml, Source};
|
||||||
|
|
||||||
|
|
@ -65,8 +65,7 @@ impl PyProjectTomlMut {
|
||||||
.ok_or(Error::MalformedWorkspace)?;
|
.ok_or(Error::MalformedWorkspace)?;
|
||||||
|
|
||||||
// Add the path to the workspace.
|
// Add the path to the workspace.
|
||||||
// Use cross-platform slashes so the toml string type does not change
|
members.push(PortablePath::from(path.as_ref()).to_string());
|
||||||
members.push(path.as_ref().to_slash_lossy().to_string());
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue