Skip to content

Commit

Permalink
Get tool.pixi.project.name from project.name
Browse files Browse the repository at this point in the history
  • Loading branch information
olivier-lacroix committed Apr 3, 2024
1 parent dd5e26d commit e23f176
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 32 deletions.
5 changes: 0 additions & 5 deletions docs/advanced/pyproject_toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ We don't advise to use the `pyproject.toml` file for anything else than python p
When you already have a `pyproject.toml` file in your project, you can add the following section to it:
```toml
[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
```
Expand All @@ -28,7 +27,6 @@ name = "my_project"
requires-python = ">=3.9"

[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
```
Expand Down Expand Up @@ -59,7 +57,6 @@ dependencies = [
]

[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]
```
Expand Down Expand Up @@ -92,7 +89,6 @@ dependencies = [
]

[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]

Expand All @@ -118,7 +114,6 @@ dependencies = [
]

[tool.pixi.project]
name = "my_project"
channels = ["conda-forge"]
platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"]

Expand Down
1 change: 0 additions & 1 deletion src/cli/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ platforms = ["{{ platforms|join("\", \"") }}"]
/// The pyproject.toml template
const PYROJECT_TEMPLATE: &str = r#"
[tool.pixi.project]
name = "{{ name }}"
channels = [{%- if channels %}"{{ channels|join("\", \"") }}"{%- endif %}]
platforms = ["{{ platforms|join("\", \"") }}"]
Expand Down
47 changes: 46 additions & 1 deletion src/project/manifest/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::project::manifest::{FeatureName, TargetSelector};
use crate::project::SpecType;
use miette::Diagnostic;
use miette::{Diagnostic, IntoDiagnostic, LabeledSpan, NamedSource, Report};
use rattler_conda_types::{InvalidPackageNameError, ParseMatchSpecError};
use thiserror::Error;

Expand Down Expand Up @@ -71,3 +71,48 @@ pub enum RequirementConversionError {
#[error("Error converting requirement from pypi to conda")]
Unimplemented,
}

#[derive(Error, Debug, Clone)]
pub enum TomlError {
#[error("failed to parse project manifest")]
Error(#[from] toml_edit::TomlError),
#[error("'pyproject.toml' should contain a [project] table")]
NoProjectTable(std::ops::Range<usize>),
#[error("The [project] table should contain a 'name'")]
NoProjectName(Option<std::ops::Range<usize>>),
}

impl TomlError {
pub fn to_fancy<T>(self, file_name: &str, contents: impl Into<String>) -> Result<T, Report> {

Check failure on line 86 in src/project/manifest/error.rs

View workflow job for this annotation

GitHub Actions / Cargo Lint

methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
if let Some(span) = self.clone().span() {
return Err(miette::miette!(

Check failure on line 88 in src/project/manifest/error.rs

View workflow job for this annotation

GitHub Actions / Cargo Lint

unneeded `return` statement
labels = vec![LabeledSpan::at(span, self.message())],
"failed to parse project manifest"
)
.with_source_code(NamedSource::new(file_name, contents.into())));
} else {
return Err(self).into_diagnostic();

Check failure on line 94 in src/project/manifest/error.rs

View workflow job for this annotation

GitHub Actions / Cargo Lint

unneeded `return` statement
}
}

fn span(self) -> Option<std::ops::Range<usize>> {
match self {
TomlError::Error(e) => e.span(),
TomlError::NoProjectTable(span) => Some(span),
TomlError::NoProjectName(span) => span,
}
}
fn message(&self) -> &str {
match self {
TomlError::Error(e) => e.message(),
TomlError::NoProjectTable(_) => "Missing field `project`",
TomlError::NoProjectName(_) => "Missing field `name`",
}
}
}

impl From<toml_edit::de::Error> for TomlError {
fn from(e: toml_edit::de::Error) -> Self {
toml_edit::TomlError::from(e).into()
}
}
2 changes: 1 addition & 1 deletion src/project/manifest/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use url::Url;
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct ProjectMetadata {
/// The name of the project
pub name: String,
pub name: Option<String>,

/// The version of the project
#[serde_as(as = "Option<DisplayFromStr>")]
Expand Down
39 changes: 21 additions & 18 deletions src/project/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use indexmap::map::Entry;
use indexmap::{Equivalent, IndexMap, IndexSet};
use itertools::Itertools;
pub use metadata::ProjectMetadata;
use miette::{miette, Diagnostic, IntoDiagnostic, LabeledSpan, NamedSource, WrapErr};
use miette::{miette, Diagnostic, IntoDiagnostic, NamedSource, WrapErr};
use pyproject::PyProjectManifest;
pub use python::PyPiRequirement;
use rattler_conda_types::{
Expand All @@ -47,7 +47,9 @@ use std::{
pub use system_requirements::{LibCSystemRequirement, SystemRequirements};
pub use target::{Target, TargetSelector, Targets};
use thiserror::Error;
use toml_edit::{DocumentMut, TomlError};
use toml_edit::DocumentMut;

use self::error::TomlError;

/// Errors that can occur when getting a feature.
#[derive(Debug, Clone, Error, Diagnostic)]
Expand Down Expand Up @@ -127,21 +129,14 @@ impl Manifest {
),
};

let (manifest, document) = match parsed
.and_then(|manifest| contents.parse::<DocumentMut>().map(|doc| (manifest, doc)))
{
let (manifest, document) = match parsed.and_then(|manifest| {
contents
.parse::<DocumentMut>()
.map(|doc| (manifest, doc))
.map_err(TomlError::from)
}) {
Ok(result) => result,
Err(e) => {
if let Some(span) = e.span() {
return Err(miette::miette!(
labels = vec![LabeledSpan::at(span, e.message())],
"failed to parse project manifest"
)
.with_source_code(NamedSource::new(file_name, contents)));
} else {
return Err(e).into_diagnostic();
}
}
Err(e) => e.to_fancy(file_name, &contents)?,
};

// Validate the contents of the manifest
Expand Down Expand Up @@ -777,7 +772,15 @@ pub struct ProjectManifest {
impl ProjectManifest {
/// Parses a toml string into a project manifest.
pub fn from_toml_str(source: &str) -> Result<Self, TomlError> {
toml_edit::de::from_str(source).map_err(TomlError::from)
let manifest: ProjectManifest = toml_edit::de::from_str(source).map_err(TomlError::from)?;

// project is not optional in pixi.toml, but name is
if manifest.project.name.is_none() {
let span = source.parse::<DocumentMut>().map_err(TomlError::from)?["project"].span();
return Err(TomlError::NoProjectName(span));
}

Ok(manifest)
}

/// Returns the default feature.
Expand Down Expand Up @@ -1118,7 +1121,7 @@ mod tests {
// From PathBuf
let manifest = Manifest::from_path(path).unwrap();

assert_eq!(manifest.parsed.project.name, "foo");
assert_eq!(manifest.parsed.project.name.unwrap(), "foo");
assert_eq!(
manifest.parsed.project.version,
Some(Version::from_str("0.1.0").unwrap())
Expand Down
20 changes: 15 additions & 5 deletions src/project/manifest/pyproject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use rattler_conda_types::{NamelessMatchSpec, PackageName, ParseStrictness::Lenie
use serde::Deserialize;
use std::str::FromStr;
use toml_edit;
use toml_edit::TomlError;

use super::{
error::RequirementConversionError, python::PyPiPackageName, ProjectManifest, PyPiRequirement,
SpecType,
error::{RequirementConversionError, TomlError},
python::PyPiPackageName,
ProjectManifest, PyPiRequirement, SpecType,
};

#[derive(Deserialize, Debug, Clone)]
Expand All @@ -33,7 +33,16 @@ impl std::ops::Deref for PyProjectManifest {
impl PyProjectManifest {
/// Parses a toml string into a pyproject manifest.
pub fn from_toml_str(source: &str) -> Result<Self, TomlError> {
toml_edit::de::from_str(source).map_err(TomlError::from)
let manifest: PyProjectManifest =
toml_edit::de::from_str(source).map_err(TomlError::from)?;

// project is optional in pyproject.toml, but name is not
// TODO: do we want to Err if tool.pixi.name is defined?
if manifest.project.is_none() {
return Err(TomlError::NoProjectTable(0..1));
}

Ok(manifest)
}
}

Expand All @@ -42,8 +51,9 @@ impl From<PyProjectManifest> for ProjectManifest {
// Start by loading the data nested under "tool.pixi"
let mut manifest = item.tool.pixi.clone();

// TODO: tool.pixi.project.name should be made optional or read from project.name
// Get tool.pixi.project.name from project.name
// TODO: could copy across / convert some other optional fields if relevant
manifest.project.name = item.project.as_ref().map(|p| p.name.clone());

// Add python as dependency based on the project.requires_python property (if any)
let pythonspec = item
Expand Down
8 changes: 7 additions & 1 deletion src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,13 @@ impl Project {

/// Returns the name of the project
pub fn name(&self) -> &str {
&self.manifest.parsed.project.name
&self

Check failure on line 223 in src/project/mod.rs

View workflow job for this annotation

GitHub Actions / Cargo Lint

this expression creates a reference which is immediately dereferenced by the compiler
.manifest
.parsed
.project
.name
.as_ref()
.expect("name should always be defined.")
}

/// Returns the version of the project
Expand Down

0 comments on commit e23f176

Please sign in to comment.