Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow parsing the tool table #21

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 91 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use indexmap::IndexMap;
use pep440_rs::{Version, VersionSpecifiers};
use pep508_rs::Requirement;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use toml::Table;

/// The `[build-system]` section of a pyproject.toml as specified in PEP 517
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
Expand All @@ -18,11 +20,13 @@ pub struct BuildSystem {
/// A pyproject.toml as specified in PEP 517
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct PyProjectToml {
pub struct PyProjectToml<T = Table> {
/// Build-related data
pub build_system: Option<BuildSystem>,
/// Project metadata
pub project: Option<Project>,
/// Tool section
pub tool: Option<T>,
}

/// PEP 621 project metadata
Expand Down Expand Up @@ -161,18 +165,40 @@ pub struct Contact {
pub email: Option<String>,
}

impl PyProjectToml {
impl<T: DeserializeOwned> PyProjectToml<T> {
/// Parse `pyproject.toml` content
pub fn new(content: &str) -> Result<Self, toml::de::Error> {
toml::de::from_str(content)
}

pub fn try_from(value: PyProjectToml<Table>) -> Result<Self, toml::de::Error> {
Ok(Self {
build_system: value.build_system,
project: value.project,
tool: value.tool.map(Table::try_into).transpose()?,
})
}
}

impl PyProjectToml {
pub fn try_into<'de, T>(self) -> Result<PyProjectToml<T>, toml::de::Error>
where
T: Deserialize<'de>,
{
Ok(PyProjectToml {
build_system: self.build_system,
project: self.project,
tool: self.tool.map(Table::try_into).transpose()?,
})
}
}

#[cfg(test)]
mod tests {
use super::{License, LicenseFiles, PyProjectToml, ReadMe};
use pep440_rs::{Version, VersionSpecifiers};
use pep508_rs::Requirement;
use serde::Deserialize;
use std::str::FromStr;

#[test]
Expand Down Expand Up @@ -228,7 +254,7 @@ spam-gui = "spam:main_gui"

[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes""#;
let project_toml = PyProjectToml::new(source).unwrap();
let project_toml: PyProjectToml = PyProjectToml::new(source).unwrap();
let build_system = &project_toml.build_system.unwrap();
assert_eq!(
build_system.requires,
Expand Down Expand Up @@ -285,7 +311,7 @@ build-backend = "maturin"
name = "spam"
license = "MIT OR BSD-3-Clause"
"#;
let project_toml = PyProjectToml::new(source).unwrap();
let project_toml: PyProjectToml = PyProjectToml::new(source).unwrap();
let project = project_toml.project.as_ref().unwrap();
assert_eq!(
project.license,
Expand All @@ -310,7 +336,7 @@ license-files.paths = [
"setuptools/_vendor/LICENSE.BSD",
]
"#;
let project_toml = PyProjectToml::new(source).unwrap();
let project_toml: PyProjectToml = PyProjectToml::new(source).unwrap();
let project = project_toml.project.as_ref().unwrap();

assert_eq!(
Expand Down Expand Up @@ -345,7 +371,7 @@ license-files.globs = [
"setuptools/_vendor/LICENSE*",
]
"#;
let project_toml = PyProjectToml::new(source).unwrap();
let project_toml: PyProjectToml = PyProjectToml::new(source).unwrap();
let project = project_toml.project.as_ref().unwrap();

assert_eq!(
Expand All @@ -372,7 +398,7 @@ build-backend = "maturin"
[project]
name = "spam"
"#;
let project_toml = PyProjectToml::new(source).unwrap();
let project_toml: PyProjectToml = PyProjectToml::new(source).unwrap();
let project = project_toml.project.as_ref().unwrap();

assert_eq!(
Expand All @@ -396,7 +422,7 @@ build-backend = "maturin"
name = "spam"
readme = {text = "ReadMe!", content-type = "text/plain"}
"#;
let project_toml = PyProjectToml::new(source).unwrap();
let project_toml: PyProjectToml = PyProjectToml::new(source).unwrap();
let project = project_toml.project.as_ref().unwrap();

assert_eq!(
Expand All @@ -408,4 +434,61 @@ readme = {text = "ReadMe!", content-type = "text/plain"}
})
);
}

#[test]
fn test_parse_pyproject_toml_tool_section_as_table() {
let source = r#"[build-system]
requires = ["maturin"]
build-backend = "maturin"

[tool.maturin]
bindings = "pyo3"
"#;
let project_toml: PyProjectToml = PyProjectToml::new(source).unwrap();
let tool = project_toml.tool.as_ref().unwrap();
assert_eq!(
tool.get("maturin")
.unwrap()
.get("bindings")
.unwrap()
.as_str(),
Some("pyo3")
);
}

#[test]
fn test_parse_pyproject_toml_tool_section() {
#[derive(Deserialize)]
struct Maturin {
bindings: Option<String>,
}

#[derive(Deserialize)]
struct Tools {
maturin: Option<Maturin>,
}

let source = r#"[build-system]
requires = ["maturin"]
build-backend = "maturin"

[tool.maturin]
bindings = "pyo3"

[tool.ruff]
line-length = 120
"#;

let project_toml: PyProjectToml<Tools> = PyProjectToml::new(source).unwrap();
assert_eq!(
project_toml
.tool
.unwrap()
.maturin
.unwrap()
.bindings
.as_deref(),
Some("pyo3")
);
}
}