use std::collections::HashSet;
use std::path::PathBuf;
use crate::pkg::reg::CachingStrategy;
use crate::pkg::repo::{PkgRepo, PkgRepoLocation};
use mcvm_core::net::download::validate_url;
use anyhow::{bail, Context};
use mcvm_shared::lang::Language;
#[cfg(feature = "schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct ConfigPreferences {
pub package_caching_strategy: CachingStrategy,
pub language: Language,
}
#[derive(Deserialize, Serialize, Default)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(default)]
pub struct PrefDeser {
pub repositories: RepositoriesDeser,
pub package_caching_strategy: CachingStrategy,
pub language: Language,
}
#[derive(Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub struct RepoDeser {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default)]
pub disable: bool,
}
#[derive(Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(default)]
pub struct RepositoriesDeser {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub preferred: Vec<RepoDeser>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub backup: Vec<RepoDeser>,
pub enable_core: bool,
pub enable_std: bool,
}
impl Default for RepositoriesDeser {
fn default() -> Self {
Self {
preferred: Vec::new(),
backup: Vec::new(),
enable_core: true,
enable_std: true,
}
}
}
impl ConfigPreferences {
pub fn read(prefs: &PrefDeser) -> anyhow::Result<(Self, Vec<PkgRepo>)> {
let mut repositories = Vec::new();
for repo in prefs.repositories.preferred.iter() {
if !repo.disable {
add_repo(&mut repositories, repo)?;
}
}
repositories.extend(PkgRepo::default_repos(
prefs.repositories.enable_core,
prefs.repositories.enable_std,
));
for repo in prefs.repositories.backup.iter() {
if !repo.disable {
add_repo(&mut repositories, repo)?;
}
}
let mut existing = HashSet::new();
for repo in &repositories {
if existing.contains(&repo.id) {
bail!("Duplicate repository ID '{}'", repo.id);
}
existing.insert(&repo.id);
}
Ok((
Self {
package_caching_strategy: prefs.package_caching_strategy.clone(),
language: prefs.language,
},
repositories,
))
}
}
fn add_repo(repos: &mut Vec<PkgRepo>, repo: &RepoDeser) -> anyhow::Result<()> {
let location = if let Some(url) = &repo.url {
validate_url(url).with_context(|| {
format!("Invalid url '{}' in package repository '{}'", url, repo.id)
})?;
PkgRepoLocation::Remote(url.clone())
} else if let Some(path) = &repo.path {
PkgRepoLocation::Local(PathBuf::from(path))
} else {
bail!("Niether path nor URL was set for repository {}", repo.id);
};
repos.push(PkgRepo::new(&repo.id, location));
Ok(())
}