Files
nye-old/nye-package/src/project/manifest.rs
T
2026-08-02 18:30:34 +00:00

136 lines
4.9 KiB
Rust

use std::path::{Path, PathBuf};
use anyhow::Context;
use semver::Version;
use serde::{Deserialize, Serialize};
use tokio::fs;
use crate::shared::Architecture;
/// A Nye package manifest.
///
/// It describes the package's general information, dependencies, exposed artifacts, and other
/// things. It's used by the Nye package manager to know how to install, update, and remove the
/// package.
///
/// This is currently stored at the package's root directory in a file called `Nye.toml`. The file
/// is in TOML format, and is deserialized into this struct when the package manager needs to read
/// it.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProjectManifest {
/// The package's general information, such as its name, version, and description.
pub package: ProjectManifestPackage,
/// The package's exposed binaries, libraries, and other artifacts.
pub exposed: ProjectManifestExposed,
}
impl ProjectManifest {
/// Validates the manifest, ensuring that the values are well-formed and consistent.
///
/// Returns:
/// * `Ok(())` - When the manifest is valid.
/// * `Err(anyhow::Error)` - When the manifest is invalid, with a message describing the issue.
pub fn validate(&self) -> anyhow::Result<()> {
if self.package.architectures.is_empty() {
anyhow::bail!("The package must support at least one architecture.");
}
for (i, architecture) in self.package.architectures.iter().enumerate() {
if self
.package
.architectures
.iter()
.position(|a| a == architecture)
!= Some(i)
{
anyhow::bail!(
"The package's architectures must be unique, yet {:?} appears more than once.",
architecture
);
}
}
Ok(())
}
}
/// A Nye package's general information, such as its name, version, and description.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProjectManifestPackage {
/// The package's name, which is used to identify it in the Nye package manager.
pub name: String,
/// The package's version, which is used to identify it in the Nye package manager.
pub version: Version,
/// The package's description, which is used to describe it in the Nye package manager.
pub description: Option<String>,
/// The package's supported architectures, which is used to determine if the package can be
/// installed on the current system.
pub architectures: Vec<Architecture>,
}
/// A Nye package's exposed binaries, libraries, and other artifacts.
///
/// Only the artifacts listed here are exposed to the user and dependants.
#[derive(Default, Clone, Serialize, Deserialize)]
pub struct ProjectManifestExposed {
/// The package's exposed binaries, which are the executables that the package provides to the
/// user.
#[serde(rename = "bin")]
pub binaries: Vec<ProjectManifestExposeBinary>,
}
/// A Nye package's exposed binary, which is an executable that the package provides to the user.
#[derive(Clone, Serialize, Deserialize)]
pub struct ProjectManifestExposeBinary {
/// The name under which the binary is exposed.
pub name: String,
/// The path to the binary, relative to the `./src/{arch}/bin` directory (which is itself
/// relative to the `Nye.toml` file).
///
/// If missing, it is assumed that the binary is located at `./src/{arch}/bin/{name}`.
pub path: Option<PathBuf>,
/// The architectures for which the binary is available.
///
/// If not specified, it is assumed that the binary is available for all architectures
/// specified in the `package.architectures` field.
pub architectures: Option<Vec<Architecture>>,
}
/// Reads a Nye package's manifest from the given path and returns it as a [`Manifest`] struct.
///
/// Arguments:
/// * `path` - The path to the manifest file. This is usually the `Nye.toml` file at the root of
/// the package's directory.
///
/// Returns:
/// * `Ok(Manifest)` - The manifest struct if the file was read and parsed successfully.
/// * `Err(anyhow::Error)` - An error if the file could not be read or parsed.
pub async fn read(path: impl AsRef<Path>) -> anyhow::Result<ProjectManifest> {
let string = fs::read_to_string(path)
.await
.context("Could not read package's manifest.")?;
parse(&string)
}
/// Parses a Nye package's manifest from the given string and returns it as a [`Manifest`] struct.
///
/// Arguments:
/// * `string` - The string containing the manifest in TOML format.
///
/// Returns:
/// * `Ok(Manifest)` - The manifest struct if the string was parsed successfully.
/// * `Err(anyhow::Error)` - An error if the string could not be parsed.
pub fn parse(string: &str) -> anyhow::Result<ProjectManifest> {
let manifest: ProjectManifest =
toml::from_str(string).context("Could not parse the package's manifest.")?;
Ok(manifest)
}