diff --git a/src/develop.rs b/src/develop.rs index d15b1b05f..84ae64222 100644 --- a/src/develop.rs +++ b/src/develop.rs @@ -9,7 +9,6 @@ use crate::Target; use anyhow::{anyhow, bail, Context, Result}; use cargo_options::heading; use fs_err as fs; -use pep508_rs::{MarkerExpression, MarkerOperator, MarkerTree, MarkerValue}; use regex::Regex; use std::path::Path; use std::path::PathBuf; @@ -145,31 +144,23 @@ fn install_dependencies( install_backend: &InstallBackend, ) -> Result<()> { if !build_context.metadata23.requires_dist.is_empty() { + let mut extra_names = Vec::with_capacity(extras.len()); + for extra in extras { + extra_names.push( + pep508_rs::ExtraName::new(extra.clone()) + .with_context(|| format!("invalid extra name: {extra}"))?, + ); + } let mut args = vec!["install".to_string()]; args.extend(build_context.metadata23.requires_dist.iter().map(|x| { let mut pkg = x.clone(); - // Remove extra marker to make it installable with pip - // Keep in sync with `Metadata23::merge_pyproject_toml()`! - for extra in extras { - pkg.marker = pkg.marker.and_then(|marker| -> Option { - match marker.clone() { - MarkerTree::Expression(MarkerExpression { - l_value: MarkerValue::Extra, - operator: MarkerOperator::Equal, - r_value: MarkerValue::QuotedString(extra_value), - }) if &extra_value == extra => None, - MarkerTree::And(and) => match &*and { - [existing, MarkerTree::Expression(MarkerExpression { - l_value: MarkerValue::Extra, - operator: MarkerOperator::Equal, - r_value: MarkerValue::QuotedString(extra_value), - })] if extra_value == extra => Some(existing.clone()), - _ => Some(marker), - }, - _ => Some(marker), - } - }); - } + // Remove extra marker to make it installable with pip: + // + // * ` and extra == 'EXTRA_NAME'` + // * `; extra == 'EXTRA_NAME'` + // + // Keep in sync with `Metadata23::merge_pyproject_toml()` + pkg.marker = pkg.marker.simplify_extras(&extra_names); pkg.to_string() })); let status = install_backend diff --git a/src/metadata.rs b/src/metadata.rs index 760343d0c..827ab828c 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -3,7 +3,9 @@ use anyhow::{bail, format_err, Context, Result}; use fs_err as fs; use indexmap::IndexMap; use pep440_rs::{Version, VersionSpecifiers}; -use pep508_rs::{MarkerExpression, MarkerOperator, MarkerTree, MarkerValue, Requirement}; +use pep508_rs::{ + ExtraName, ExtraOperator, MarkerExpression, MarkerTree, MarkerValueExtra, Requirement, +}; use pyproject_toml::License; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -291,16 +293,14 @@ impl Metadata23 { for dep in deps { let mut dep = dep.clone(); // Keep in sync with `develop()`! - let new_extra = MarkerTree::Expression(MarkerExpression { - l_value: MarkerValue::Extra, - operator: MarkerOperator::Equal, - r_value: MarkerValue::QuotedString(extra.to_string()), - }); - if let Some(existing) = dep.marker.take() { - dep.marker = Some(MarkerTree::And(vec![existing, new_extra])); - } else { - dep.marker = Some(new_extra); - } + let new_extra = MarkerExpression::Extra { + operator: ExtraOperator::Equal, + name: MarkerValueExtra::Extra( + ExtraName::new(extra.clone()) + .with_context(|| format!("invalid extra name: {extra}"))?, + ), + }; + dep.marker.and(MarkerTree::expression(new_extra)); self.requires_dist.push(dep); } } diff --git a/src/pyproject_toml.rs b/src/pyproject_toml.rs index f338b03ed..b4cede944 100644 --- a/src/pyproject_toml.rs +++ b/src/pyproject_toml.rs @@ -627,11 +627,13 @@ mod tests { let inner_error = outer_error.source().unwrap(); let expected = expect![[r#" - TOML parse error at line 7, column 17 - | - 7 | license-files = [ "license.txt",] - | ^^^^^^^^^^^^^^^^^ - wanted string or table + TOML parse error at line 10, column 16 + | + 10 | dependencies = [ "packaging", "...",] + | ^^^^^^^^^^^^^^^^^^^^^^ + URL requirement must be preceded by a package name. Add the name of the package before the URL (e.g., `package_name @ /path/to/file`). + ... + ^^^ "#]]; expected.assert_eq(&inner_error.to_string()); }