Skip to content

Commit

Permalink
feat: Add support for additional package manifest keys (#6868)
Browse files Browse the repository at this point in the history
## Description

Adds new keys to the package manifest's project section. These will be
used when publishing packages.

## Checklist

- [ ] I have linked to any relevant issues.
- [ ] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [ ] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [ ] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [ ] I have requested a review from the relevant team or maintainers.
  • Loading branch information
sdankel authored Jan 29, 2025
1 parent 1194375 commit 1762714
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 16 deletions.
17 changes: 16 additions & 1 deletion docs/book/src/forc/manifest_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ The `Forc.toml` (the _manifest_ file) is a compulsory file for each package and

* [`[project]`](#the-project-section) — Defines a sway project.
* `name` — The name of the project.
* `version` — The version of the project.
* `description` — A description of the project.
* `authors` — The authors of the project.
* `organization` — The organization of the project.
* `license`— The project license.
* `license` — The project license.
* `homepage` — URL of the project homepage.
* `repository` — URL of the project source repository.
* `documentation` — URL of the project documentation.
* `entry` — The entry point for the compiler to start parsing from.
* For the recommended way of selecting an entry point of large libraries please take a look at: [Libraries](./../sway-program-types/libraries.md)
* `implicit-std` - Controls whether provided `std` version (with the current `forc` version) will get added as a dependency _implicitly_. _Unless you know what you are doing, leave this as default._
Expand All @@ -29,6 +34,11 @@ An example `Forc.toml` is shown below. Under `[project]` the following fields ar

* `authors`
* `organization`
* `version`
* `description`
* `homepage`
* `repository`
* `documentation`

Also for the following fields, a default value is provided so omitting them is allowed:

Expand All @@ -39,6 +49,11 @@ Also for the following fields, a default value is provided so omitting them is a
[project]
authors = ["user"]
entry = "main.sw"
description = "Wallet contract"
version = "1.0.0"
homepage = "https://example.com/"
repository = "https://example.com/"
documentation = "https://example.com/"
organization = "Fuel_Labs"
license = "Apache-2.0"
name = "wallet_contract"
Expand Down
81 changes: 66 additions & 15 deletions forc-pkg/src/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use crate::pkg::{manifest_file_missing, parsing_failed, wrong_program_type};
use anyhow::{anyhow, bail, Context, Result};
use forc_tracing::println_warning;
use forc_util::{validate_name, validate_project_name};
use serde::{Deserialize, Serialize};
use semver::Version;
use serde::{de, Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
use std::{
collections::{BTreeMap, HashMap},
Expand All @@ -19,6 +20,7 @@ use sway_utils::{
constants, find_nested_manifest_dir, find_parent_manifest_dir,
find_parent_manifest_dir_with_check,
};
use url::Url;

use self::build_profile::BuildProfile;

Expand Down Expand Up @@ -193,9 +195,15 @@ pub struct PackageManifest {
#[serde(rename_all = "kebab-case")]
pub struct Project {
pub authors: Option<Vec<String>>,
#[serde(deserialize_with = "validate_package_name")]
pub name: String,
pub version: Option<Version>,
pub description: Option<String>,
pub organization: Option<String>,
pub license: String,
pub homepage: Option<Url>,
pub repository: Option<Url>,
pub documentation: Option<Url>,
#[serde(default = "default_entry")]
pub entry: String,
pub implicit_std: Option<bool>,
Expand All @@ -205,6 +213,18 @@ pub struct Project {
pub metadata: Option<toml::Value>,
}

// Validation function for the `name` field
fn validate_package_name<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: de::Deserializer<'de>,
{
let name: String = Deserialize::deserialize(deserializer)?;
match validate_project_name(&name) {
Ok(_) => Ok(name),
Err(e) => Err(de::Error::custom(e.to_string())),
}
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct Network {
Expand Down Expand Up @@ -318,6 +338,14 @@ impl Dependency {
Self::Detailed(ref det) => det.package.as_deref(),
}
}

/// The string of the `version` field if specified.
pub fn version(&self) -> Option<&str> {
match *self {
Self::Simple(ref version) => Some(version),
Self::Detailed(ref det) => det.version.as_deref(),
}
}
}

impl PackageManifestFile {
Expand Down Expand Up @@ -571,10 +599,25 @@ impl PackageManifest {
// package or a workspace. While doing so, we should be printing the warnings if the given
// file parses so that we only see warnings for the correct type of manifest.
let path = path.as_ref();
let mut warnings = vec![];
let manifest_str = std::fs::read_to_string(path)
let contents = std::fs::read_to_string(path)
.map_err(|e| anyhow!("failed to read manifest at {:?}: {}", path, e))?;
let toml_de = toml::de::Deserializer::new(&manifest_str);
Self::from_string(contents)
}

/// Given a path to a `Forc.toml`, read it and construct a `PackageManifest`.
///
/// This also `validate`s the manifest, returning an `Err` in the case that invalid names,
/// fields were used.
///
/// If `core` and `std` are unspecified, `std` will be added to the `dependencies` table
/// implicitly. In this case, the git tag associated with the version of this crate is used to
/// specify the pinned commit at which we fetch `std`.
pub fn from_string(contents: String) -> Result<Self> {
// While creating a `ManifestFile` we need to check if the given path corresponds to a
// package or a workspace. While doing so, we should be printing the warnings if the given
// file parses so that we only see warnings for the correct type of manifest.
let mut warnings = vec![];
let toml_de = toml::de::Deserializer::new(&contents);
let mut manifest: Self = serde_ignored::deserialize(toml_de, |path| {
let warning = format!("unused manifest key: {path}");
warnings.push(warning);
Expand Down Expand Up @@ -1338,6 +1381,11 @@ mod tests {
let project = Project {
authors: Some(vec!["Test Author".to_string()]),
name: "test-project".to_string(),
version: Some(Version::parse("0.1.0").unwrap()),
description: Some("test description".to_string()),
homepage: None,
documentation: None,
repository: None,
organization: None,
license: "Apache-2.0".to_string(),
entry: "main.sw".to_string(),
Expand All @@ -1359,6 +1407,11 @@ mod tests {
let project = Project {
authors: Some(vec!["Test Author".to_string()]),
name: "test-project".to_string(),
version: Some(Version::parse("0.1.0").unwrap()),
description: Some("test description".to_string()),
homepage: Some(Url::parse("https://example.com").unwrap()),
documentation: Some(Url::parse("https://docs.example.com").unwrap()),
repository: Some(Url::parse("https://example.com").unwrap()),
organization: None,
license: "Apache-2.0".to_string(),
entry: "main.sw".to_string(),
Expand All @@ -1372,6 +1425,11 @@ mod tests {
let deserialized: Project = toml::from_str(&serialized).unwrap();

assert_eq!(project.name, deserialized.name);
assert_eq!(project.version, deserialized.version);
assert_eq!(project.description, deserialized.description);
assert_eq!(project.homepage, deserialized.homepage);
assert_eq!(project.documentation, deserialized.documentation);
assert_eq!(project.repository, deserialized.repository);
assert_eq!(project.metadata, deserialized.metadata);
assert_eq!(project.metadata, None);
}
Expand All @@ -1383,13 +1441,11 @@ mod tests {
license = "Apache-2.0"
entry = "main.sw"
authors = ["Test Author"]
[metadata]
description = "A test project"
version = "1.0.0"
homepage = "https://example.com"
documentation = "https://docs.example.com"
repository = "https://github.com/example/test-project"
[metadata]
mykey = "https://example.com"
keywords = ["test", "project"]
categories = ["test"]
"#;
Expand All @@ -1401,12 +1457,7 @@ mod tests {
let table = metadata.as_table().unwrap();

assert_eq!(
table.get("description").unwrap().as_str().unwrap(),
"A test project"
);
assert_eq!(table.get("version").unwrap().as_str().unwrap(), "1.0.0");
assert_eq!(
table.get("homepage").unwrap().as_str().unwrap(),
table.get("mykey").unwrap().as_str().unwrap(),
"https://example.com"
);

Expand Down

0 comments on commit 1762714

Please sign in to comment.