diff --git a/Cargo.lock b/Cargo.lock index 573984b..76bfcba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ dependencies = [ [[package]] name = "pyproject-toml" -version = "0.11.0" +version = "0.12.0" dependencies = [ "indexmap", "pep440_rs", diff --git a/Cargo.toml b/Cargo.toml index d3d2173..89d0baf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyproject-toml" -version = "0.11.0" +version = "0.12.0" description = "pyproject.toml parser in Rust" edition = "2021" license = "MIT" diff --git a/Changelog.md b/Changelog.md index 32855f1..97c7f5f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # Changelog +## 0.12.0 + +* Support dependency groups (PEP 735) + ## 0.11.0 * Update pep440_rs to 0.6.0 diff --git a/src/lib.rs b/src/lib.rs index b967d18..1e0678b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ use indexmap::IndexMap; use pep440_rs::{Version, VersionSpecifiers}; use pep508_rs::Requirement; use serde::{Deserialize, Serialize}; +use std::ops::Deref; /// The `[build-system]` section of a pyproject.toml as specified in PEP 517 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] @@ -23,6 +24,8 @@ pub struct PyProjectToml { pub build_system: Option, /// Project metadata pub project: Option, + /// Dependency groups table + pub dependency_groups: Option, } /// PEP 621 project metadata @@ -161,6 +164,33 @@ pub struct Contact { pub email: Option, } +/// The `[dependency-groups]` section of pyproject.toml, as specified in PEP 735 +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(transparent)] +pub struct DependencyGroups(pub IndexMap>); + +impl Deref for DependencyGroups { + type Target = IndexMap>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// A specifier item in a Dependency Group +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "kebab-case", untagged)] +pub enum DependencyGroupSpecifier { + /// PEP 508 requirement string + String(Requirement), + /// Include another dependency group + #[serde(rename_all = "kebab-case")] + Table { + /// The name of the group to include + include_group: Option, + }, +} + impl PyProjectToml { /// Parse `pyproject.toml` content pub fn new(content: &str) -> Result { @@ -170,7 +200,7 @@ impl PyProjectToml { #[cfg(test)] mod tests { - use super::{License, LicenseFiles, PyProjectToml, ReadMe}; + use super::{DependencyGroupSpecifier, License, LicenseFiles, PyProjectToml, ReadMe}; use pep440_rs::{Version, VersionSpecifiers}; use pep508_rs::Requirement; use std::str::FromStr; @@ -408,4 +438,39 @@ readme = {text = "ReadMe!", content-type = "text/plain"} }) ); } + + #[test] + fn test_parse_pyproject_toml_dependency_groups() { + let source = r#"[dependency-groups] +alpha = ["beta", "gamma", "delta"] +epsilon = ["eta<2.0", "theta==2024.09.01"] +iota = [{include-group = "alpha"}] +"#; + let project_toml = PyProjectToml::new(source).unwrap(); + let dependency_groups = project_toml.dependency_groups.as_ref().unwrap(); + + assert_eq!( + dependency_groups["alpha"], + vec![ + DependencyGroupSpecifier::String(Requirement::from_str("beta").unwrap()), + DependencyGroupSpecifier::String(Requirement::from_str("gamma").unwrap()), + DependencyGroupSpecifier::String(Requirement::from_str("delta").unwrap(),) + ] + ); + assert_eq!( + dependency_groups["epsilon"], + vec![ + DependencyGroupSpecifier::String(Requirement::from_str("eta<2.0").unwrap()), + DependencyGroupSpecifier::String( + Requirement::from_str("theta==2024.09.01").unwrap() + ) + ] + ); + assert_eq!( + dependency_groups["iota"], + vec![DependencyGroupSpecifier::Table { + include_group: Some("alpha".to_string()) + }] + ); + } }