136 lines
4.9 KiB
Rust
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)
|
|
}
|