featurecomb-schema 0.1.0

Schema of featurecomb's metadata subtable
Documentation
//! The schema for the custom
//! [`[metadata]`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-metadata-table)
//! subtable for featurecomb.

use indexmap::IndexMap;
use serde::{Deserialize, Serialize};

/// Represents the custom [`[metadata]`
/// section](https://doc.rust-lang.org/cargo/reference/manifest.html#the-metadata-table) of a crate
/// manifest.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Metadata {
    /// Custom subtable for featurecomb.
    pub feature_groups: Option<FeatureGroups>,
}

/// Custom
/// [`[metadata]`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-metadata-table)
/// subtable for featurecomb.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct FeatureGroups {
    /// Features.
    pub features: Option<FeaturesTable>,
    /// Feature groups.
    #[serde(flatten)]
    pub groups: IndexMap<FeatureGroupName, FeatureGroup>,
}

impl FeatureGroups {
    /// Returns an iterator over features that are part of the given feature group.
    pub fn features_in_group<'a>(
        &'a self,
        feature_group: &FeatureGroupName,
    ) -> impl Iterator<Item = &'a FeatureName> {
        self.groups
            .get(feature_group)
            .into_iter()
            .flat_map(|g| g.features().into_iter().flat_map(|f| f.iter()))
    }
}

/// A table similar to
/// [`[features]`](https://doc.rust-lang.org/cargo/reference/features.html#the-features-section),
/// with custom values.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct FeaturesTable {
    #[serde(flatten)]
    features: IndexMap<FeatureName, FeatureRelations>,
}

impl FeaturesTable {
    /// Returns an iterator over the features names, i.e., the table's keys.
    pub fn feature_names(&self) -> impl Iterator<Item = &FeatureName> {
        self.features.keys()
    }

    /// Returns an iterator over the table entries.
    pub fn iter(&self) -> impl Iterator<Item = (&FeatureName, &FeatureRelations)> {
        self.features.iter()
    }
}

/// Defines a feature group.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "kebab-case")]
pub enum FeatureGroup {
    /// Defines a feature group whose features are mutually exclusive and one must always
    /// be enabled.
    ExactlyOne {
        /// Features of the group.
        features: Vec<FeatureName>,
    },
    /// Defines a feature group whose features are mutually exclusive.
    Xor {
        /// Features of the group.
        features: Vec<FeatureName>,
    },
    /// Defines a feature group with no relations between its features.
    #[serde(untagged)]
    Or {
        /// Features of the group.
        features: Vec<FeatureName>,
    },
}

impl FeatureGroup {
    /// Returns the features part of the feature group.
    /// Returns `None` when the variant contains no features.
    ///
    /// The relation between these features depends on the feature group variant.
    #[must_use]
    pub fn features(&self) -> Option<&Vec<FeatureName>> {
        match self {
            Self::ExactlyOne { features, .. }
            | Self::Or { features, .. }
            | Self::Xor { features, .. } => Some(features),
        }
    }
}

/// A feature group name.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FeatureGroupName(String);

impl FeatureGroupName {
    /// Returns the feature group name.
    #[must_use]
    pub fn name(&self) -> &str {
        &self.0
    }
}

impl std::fmt::Display for FeatureGroupName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// A feature name.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FeatureName(String);

impl FeatureName {
    /// Returns the feature name.
    #[must_use]
    pub fn name(&self) -> &str {
        &self.0
    }
}

impl std::fmt::Display for FeatureName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Represents the relations defined for a feature.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "kebab-case")]
pub enum FeatureRelations {
    /// Defines the features and feature groups that a features *requires*.
    Requires {
        /// Features *required* by the feature.
        // Uses an Option similarly to
        // https://docs.rs/cargo-util-schemas/latest/cargo_util_schemas/manifest/struct.TomlDetailedDependency.html#structfield.features
        features: Option<Vec<FeatureName>>,
        /// Feature groups *required* by the feature.
        groups: Option<Vec<FeatureGroupName>>,
    },
}