use std::fmt::Display;
use std::sync::Arc;
use anyhow::{bail, ensure};
use mcvm_pkg::properties::PackageProperties;
use mcvm_pkg::PackageContentType;
use mcvm_shared::pkg::{is_valid_package_id, ArcPkgReq, PackageID, PackageStability};
use mcvm_shared::util::is_valid_identifier;
use serde::{Deserialize, Serialize};
use crate::package::eval::EvalPermissions;
use mcvm_pkg::{PkgRequest, PkgRequestSource};
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(untagged)]
pub enum PackageConfig {
Basic(PackageID),
Full(FullPackageConfig),
}
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(untagged)]
#[serde(rename_all = "snake_case")]
pub enum FullPackageConfig {
Local {
r#type: PackageType,
id: PackageID,
#[serde(default)]
content_type: PackageContentType,
path: String,
#[serde(default)]
features: Vec<String>,
#[serde(default = "use_default_features_default")]
use_default_features: bool,
#[serde(default)]
permissions: EvalPermissions,
#[serde(default)]
stability: Option<PackageStability>,
},
Repository {
id: PackageID,
#[serde(default)]
features: Vec<String>,
#[serde(default = "use_default_features_default")]
use_default_features: bool,
#[serde(default)]
permissions: EvalPermissions,
#[serde(default)]
stability: Option<PackageStability>,
},
}
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PackageType {
Local,
}
fn use_default_features_default() -> bool {
true
}
impl Display for PackageConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Basic(id) => id,
Self::Full(FullPackageConfig::Local { id, .. }) => id,
Self::Full(FullPackageConfig::Repository { id, .. }) => id,
}
)
}
}
impl PackageConfig {
pub fn get_pkg_id(&self) -> PackageID {
match &self {
Self::Basic(id) => id.clone(),
Self::Full(cfg) => match cfg {
FullPackageConfig::Local { id, .. } => id.clone(),
FullPackageConfig::Repository { id, .. } => id.clone(),
},
}
}
pub fn get_request(&self) -> ArcPkgReq {
let id = self.get_pkg_id();
Arc::new(PkgRequest::new(id.clone(), PkgRequestSource::UserRequire))
}
pub fn get_features(&self) -> Vec<String> {
match &self {
Self::Basic(..) => Vec::new(),
Self::Full(cfg) => match cfg {
FullPackageConfig::Local { features, .. } => features.clone(),
FullPackageConfig::Repository { features, .. } => features.clone(),
},
}
}
pub fn get_use_default_features(&self) -> bool {
match &self {
Self::Basic(..) => use_default_features_default(),
Self::Full(cfg) => match cfg {
FullPackageConfig::Local {
use_default_features,
..
} => *use_default_features,
FullPackageConfig::Repository {
use_default_features,
..
} => *use_default_features,
},
}
}
pub fn get_permissions(&self) -> EvalPermissions {
match &self {
Self::Basic(..) => EvalPermissions::Standard,
Self::Full(cfg) => match cfg {
FullPackageConfig::Local { permissions, .. } => *permissions,
FullPackageConfig::Repository { permissions, .. } => *permissions,
},
}
}
pub fn get_stability(&self, profile_stability: PackageStability) -> PackageStability {
match &self {
Self::Basic(..) => profile_stability,
Self::Full(cfg) => match cfg {
FullPackageConfig::Local { stability, .. } => {
stability.unwrap_or(profile_stability)
}
FullPackageConfig::Repository { stability, .. } => {
stability.unwrap_or(profile_stability)
}
},
}
}
pub fn calculate_features(
&self,
properties: &PackageProperties,
) -> anyhow::Result<Vec<String>> {
let allowed_features = properties.features.clone().unwrap_or_default();
let default_features = properties.default_features.clone().unwrap_or_default();
let features = self.get_features();
for feature in &features {
ensure!(
allowed_features.contains(feature),
"Configured feature '{feature}' does not exist"
);
}
let mut out = Vec::new();
if self.get_use_default_features() {
out.extend(default_features);
}
out.extend(features);
Ok(out)
}
pub fn validate(&self) -> anyhow::Result<()> {
let id = self.get_pkg_id();
if !is_valid_package_id(&id) {
bail!("Invalid package ID '{id}'");
}
for feature in self.get_features() {
if !is_valid_identifier(&feature) {
bail!("Invalid string '{feature}'");
}
}
Ok(())
}
}