mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Avoid using owned String
for package name constructors (#11768)
## Summary Since we use `SmallString` internally, there's no benefit to passing an owned string to the `PackageName` constructor (same goes for `ExtraName`, etc.). I've kept them for now (maybe that will change in the future, so it's useful to have clients passed own values if they _can_), but removed a bunch of usages where we were casting from `&str` to `String` needlessly to use the constructor.
This commit is contained in:
parent
6080dcbf7b
commit
76c3caf24f
11 changed files with 54 additions and 60 deletions
|
@ -95,7 +95,7 @@ mod tests {
|
|||
Vec::new()
|
||||
);
|
||||
($($x:expr),+ $(,)?) => (
|
||||
vec![$(ExtraName::new($x.into()).unwrap()),+]
|
||||
vec![$(ExtraName::from_owned($x.into()).unwrap()),+]
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize};
|
|||
|
||||
use uv_small_str::SmallString;
|
||||
|
||||
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
|
||||
use crate::{validate_and_normalize_ref, InvalidNameError};
|
||||
|
||||
/// The normalized name of an extra dependency.
|
||||
///
|
||||
|
@ -22,8 +22,11 @@ pub struct ExtraName(SmallString);
|
|||
|
||||
impl ExtraName {
|
||||
/// Create a validated, normalized extra name.
|
||||
pub fn new(name: String) -> Result<Self, InvalidNameError> {
|
||||
validate_and_normalize_owned(name).map(Self)
|
||||
///
|
||||
/// At present, this is no more efficient than calling [`ExtraName::from_str`].
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn from_owned(name: String) -> Result<Self, InvalidNameError> {
|
||||
validate_and_normalize_ref(&name).map(Self)
|
||||
}
|
||||
|
||||
/// Return the underlying extra name as a string.
|
||||
|
|
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|||
|
||||
use uv_small_str::SmallString;
|
||||
|
||||
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
|
||||
use crate::{validate_and_normalize_ref, InvalidNameError};
|
||||
|
||||
/// The normalized name of a dependency group.
|
||||
///
|
||||
|
@ -20,8 +20,11 @@ pub struct GroupName(SmallString);
|
|||
|
||||
impl GroupName {
|
||||
/// Create a validated, normalized group name.
|
||||
pub fn new(name: String) -> Result<Self, InvalidNameError> {
|
||||
validate_and_normalize_owned(name).map(Self)
|
||||
///
|
||||
/// At present, this is no more efficient than calling [`GroupName::from_str`].
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn from_owned(name: String) -> Result<Self, InvalidNameError> {
|
||||
validate_and_normalize_ref(&name).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,4 +72,4 @@ impl AsRef<str> for GroupName {
|
|||
/// Internally, we model dependency groups as a generic concept; but externally, we only expose the
|
||||
/// `dev-dependencies` group.
|
||||
pub static DEV_DEPENDENCIES: LazyLock<GroupName> =
|
||||
LazyLock::new(|| GroupName::new("dev".to_string()).unwrap());
|
||||
LazyLock::new(|| GroupName::from_str("dev").unwrap());
|
||||
|
|
|
@ -13,15 +13,6 @@ mod extra_name;
|
|||
mod group_name;
|
||||
mod package_name;
|
||||
|
||||
/// Validate and normalize an owned package or extra name.
|
||||
pub(crate) fn validate_and_normalize_owned(name: String) -> Result<SmallString, InvalidNameError> {
|
||||
if is_normalized(&name)? {
|
||||
Ok(SmallString::from(name))
|
||||
} else {
|
||||
Ok(SmallString::from(normalize(&name)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate and normalize an unowned package or extra name.
|
||||
pub(crate) fn validate_and_normalize_ref(
|
||||
name: impl AsRef<str>,
|
||||
|
@ -151,12 +142,6 @@ mod tests {
|
|||
validate_and_normalize_ref(input).unwrap().as_ref(),
|
||||
"friendly-bard"
|
||||
);
|
||||
assert_eq!(
|
||||
validate_and_normalize_owned(input.to_string())
|
||||
.unwrap()
|
||||
.as_ref(),
|
||||
"friendly-bard"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,12 +171,6 @@ mod tests {
|
|||
let unchanged = ["friendly-bard", "1okay", "okay2"];
|
||||
for input in unchanged {
|
||||
assert_eq!(validate_and_normalize_ref(input).unwrap().as_ref(), input);
|
||||
assert_eq!(
|
||||
validate_and_normalize_owned(input.to_string())
|
||||
.unwrap()
|
||||
.as_ref(),
|
||||
input
|
||||
);
|
||||
assert!(is_normalized(input).unwrap());
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +188,6 @@ mod tests {
|
|||
];
|
||||
for input in failures {
|
||||
assert!(validate_and_normalize_ref(input).is_err());
|
||||
assert!(validate_and_normalize_owned(input.to_string()).is_err());
|
||||
assert!(is_normalized(input).is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize};
|
|||
|
||||
use uv_small_str::SmallString;
|
||||
|
||||
use crate::{validate_and_normalize_owned, validate_and_normalize_ref, InvalidNameError};
|
||||
use crate::{validate_and_normalize_ref, InvalidNameError};
|
||||
|
||||
/// The normalized name of a package.
|
||||
///
|
||||
|
@ -33,8 +33,11 @@ pub struct PackageName(SmallString);
|
|||
|
||||
impl PackageName {
|
||||
/// Create a validated, normalized package name.
|
||||
pub fn new(name: String) -> Result<Self, InvalidNameError> {
|
||||
validate_and_normalize_owned(name).map(Self)
|
||||
///
|
||||
/// At present, this is no more efficient than calling [`PackageName::from_str`].
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn from_owned(name: String) -> Result<Self, InvalidNameError> {
|
||||
validate_and_normalize_ref(&name).map(Self)
|
||||
}
|
||||
|
||||
/// Escape this name with underscores (`_`) instead of dashes (`-`)
|
||||
|
|
|
@ -625,7 +625,8 @@ fn parse_extras_cursor<T: Pep508Url>(
|
|||
|
||||
// Add the parsed extra
|
||||
extras.push(
|
||||
ExtraName::new(buffer).expect("`ExtraName` validation should match PEP 508 parsing"),
|
||||
ExtraName::from_str(&buffer)
|
||||
.expect("`ExtraName` validation should match PEP 508 parsing"),
|
||||
);
|
||||
is_first_iteration = false;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ impl Metadata10 {
|
|||
/// Parse the [`Metadata10`] from a `PKG-INFO` file, as included in a source distribution.
|
||||
pub fn parse_pkg_info(content: &[u8]) -> Result<Self, MetadataError> {
|
||||
let headers = Headers::parse(content)?;
|
||||
let name = PackageName::new(
|
||||
let name = PackageName::from_owned(
|
||||
headers
|
||||
.get_first_value("Name")
|
||||
.ok_or(MetadataError::FieldNotFound("Name"))?,
|
||||
|
|
|
@ -40,7 +40,7 @@ impl ResolutionMetadata {
|
|||
pub fn parse_metadata(content: &[u8]) -> Result<Self, MetadataError> {
|
||||
let headers = Headers::parse(content)?;
|
||||
|
||||
let name = PackageName::new(
|
||||
let name = PackageName::from_owned(
|
||||
headers
|
||||
.get_first_value("Name")
|
||||
.ok_or(MetadataError::FieldNotFound("Name"))?,
|
||||
|
@ -63,13 +63,15 @@ impl ResolutionMetadata {
|
|||
.map(VersionSpecifiers::from);
|
||||
let provides_extras = headers
|
||||
.get_all_values("Provides-Extra")
|
||||
.filter_map(|provides_extra| match ExtraName::new(provides_extra) {
|
||||
Ok(extra_name) => Some(extra_name),
|
||||
Err(err) => {
|
||||
warn!("Ignoring invalid extra: {err}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(
|
||||
|provides_extra| match ExtraName::from_owned(provides_extra) {
|
||||
Ok(extra_name) => Some(extra_name),
|
||||
Err(err) => {
|
||||
warn!("Ignoring invalid extra: {err}");
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
let dynamic = headers
|
||||
.get_all_values("Dynamic")
|
||||
|
@ -116,7 +118,7 @@ impl ResolutionMetadata {
|
|||
}
|
||||
|
||||
// The `Name` and `Version` fields are required, and can't be dynamic.
|
||||
let name = PackageName::new(
|
||||
let name = PackageName::from_owned(
|
||||
headers
|
||||
.get_first_value("Name")
|
||||
.ok_or(MetadataError::FieldNotFound("Name"))?,
|
||||
|
@ -141,13 +143,15 @@ impl ResolutionMetadata {
|
|||
.map(VersionSpecifiers::from);
|
||||
let provides_extras = headers
|
||||
.get_all_values("Provides-Extra")
|
||||
.filter_map(|provides_extra| match ExtraName::new(provides_extra) {
|
||||
Ok(extra_name) => Some(extra_name),
|
||||
Err(err) => {
|
||||
warn!("Ignoring invalid extra: {err}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(
|
||||
|provides_extra| match ExtraName::from_owned(provides_extra) {
|
||||
Ok(extra_name) => Some(extra_name),
|
||||
Err(err) => {
|
||||
warn!("Ignoring invalid extra: {err}");
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(Self {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::str::FromStr;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||
use uv_pep508::{
|
||||
ExtraOperator, MarkerEnvironment, MarkerEnvironmentBuilder, MarkerExpression, MarkerOperator,
|
||||
|
@ -506,7 +508,7 @@ fn encode_package_extra(package: &PackageName, extra: &ExtraName) -> ExtraName {
|
|||
// character in `package` or `extra` values. But if we know the length of
|
||||
// the package name, we can always parse each field unambiguously.
|
||||
let package_len = package.as_str().len();
|
||||
ExtraName::new(format!("extra-{package_len}-{package}-{extra}")).unwrap()
|
||||
ExtraName::from_owned(format!("extra-{package_len}-{package}-{extra}")).unwrap()
|
||||
}
|
||||
|
||||
/// Encodes the given package name and its corresponding group into a valid
|
||||
|
@ -514,7 +516,7 @@ fn encode_package_extra(package: &PackageName, extra: &ExtraName) -> ExtraName {
|
|||
fn encode_package_group(package: &PackageName, group: &GroupName) -> ExtraName {
|
||||
// See `encode_package_extra`, the same considerations apply here.
|
||||
let package_len = package.as_str().len();
|
||||
ExtraName::new(format!("group-{package_len}-{package}-{group}")).unwrap()
|
||||
ExtraName::from_owned(format!("group-{package_len}-{package}-{group}")).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -583,7 +585,7 @@ impl<'a> ParsedRawExtra<'a> {
|
|||
}
|
||||
|
||||
fn to_conflict_item(&self) -> Result<ConflictItem, ResolveError> {
|
||||
let package = PackageName::new(self.package().to_string()).map_err(|name_error| {
|
||||
let package = PackageName::from_str(self.package()).map_err(|name_error| {
|
||||
ResolveError::InvalidValueInConflictMarker {
|
||||
kind: "package",
|
||||
name_error,
|
||||
|
@ -591,7 +593,7 @@ impl<'a> ParsedRawExtra<'a> {
|
|||
})?;
|
||||
match *self {
|
||||
ParsedRawExtra::Extra { extra, .. } => {
|
||||
let extra = ExtraName::new(extra.to_string()).map_err(|name_error| {
|
||||
let extra = ExtraName::from_str(extra).map_err(|name_error| {
|
||||
ResolveError::InvalidValueInConflictMarker {
|
||||
kind: "extra",
|
||||
name_error,
|
||||
|
@ -600,7 +602,7 @@ impl<'a> ParsedRawExtra<'a> {
|
|||
Ok(ConflictItem::from((package, extra)))
|
||||
}
|
||||
ParsedRawExtra::Group { group, .. } => {
|
||||
let group = GroupName::new(group.to_string()).map_err(|name_error| {
|
||||
let group = GroupName::from_str(group).map_err(|name_error| {
|
||||
ResolveError::InvalidValueInConflictMarker {
|
||||
kind: "group",
|
||||
name_error,
|
||||
|
@ -760,7 +762,7 @@ mod tests {
|
|||
|
||||
/// Shortcut for creating an extra name.
|
||||
fn create_extra(name: &str) -> ExtraName {
|
||||
ExtraName::new(name.to_string()).unwrap()
|
||||
ExtraName::from_str(name).unwrap()
|
||||
}
|
||||
|
||||
/// Shortcut for creating a conflict marker from an extra name.
|
||||
|
|
|
@ -861,7 +861,7 @@ impl PyProjectTomlMut {
|
|||
let Some(dependencies) = dependencies.as_array() else {
|
||||
continue;
|
||||
};
|
||||
let Ok(extra) = ExtraName::new(extra.to_string()) else {
|
||||
let Ok(extra) = ExtraName::from_str(extra) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
@ -878,7 +878,7 @@ impl PyProjectTomlMut {
|
|||
let Some(dependencies) = dependencies.as_array() else {
|
||||
continue;
|
||||
};
|
||||
let Ok(group) = GroupName::new(group.to_string()) else {
|
||||
let Ok(group) = GroupName::from_str(group) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ pub(crate) async fn init(
|
|||
// Pre-normalize the package name by removing any leading or trailing
|
||||
// whitespace, and replacing any internal whitespace with hyphens.
|
||||
let name = name.trim().replace(' ', "-");
|
||||
PackageName::new(name)?
|
||||
PackageName::from_owned(name)?
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue