use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use url::Url;
use crate::config::{Config, MergeError, ValidationError};
#[cfg(feature = "edit")]
use crate::edit::ConfigEditError;
#[derive(Clone, Default, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct RepodataChannelConfig {
#[serde(alias = "disable_bzip2")] #[serde(skip_serializing_if = "Option::is_none")]
pub disable_bzip2: Option<bool>,
#[serde(alias = "disable_zstd")] #[serde(skip_serializing_if = "Option::is_none")]
pub disable_zstd: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_sharded: Option<bool>,
}
impl RepodataChannelConfig {
pub fn is_empty(&self) -> bool {
self.disable_bzip2.is_none()
&& self.disable_zstd.is_none()
&& self.disable_sharded.is_none()
}
pub fn merge(&self, other: Self) -> Self {
Self {
disable_zstd: self.disable_zstd.or(other.disable_zstd),
disable_bzip2: self.disable_bzip2.or(other.disable_bzip2),
disable_sharded: self.disable_sharded.or(other.disable_sharded),
}
}
}
#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct RepodataConfig {
#[serde(flatten)]
pub default: RepodataChannelConfig,
#[serde(flatten)]
pub per_channel: HashMap<Url, RepodataChannelConfig>,
}
impl RepodataConfig {
pub fn is_empty(&self) -> bool {
self.default.is_empty() && self.per_channel.is_empty()
}
}
impl Config for RepodataConfig {
fn get_extension_name(&self) -> String {
"repodata".to_string()
}
fn merge_config(self, other: &Self) -> Result<Self, MergeError> {
let mut merged = self.clone();
merged.default = merged.default.merge(other.default.clone());
for (url, config) in &other.per_channel {
merged
.per_channel
.entry(url.clone())
.and_modify(|existing| *existing = existing.merge(config.clone()))
.or_insert_with(|| config.clone());
}
Ok(merged)
}
fn validate(&self) -> Result<(), ValidationError> {
if self.default.is_empty() && self.per_channel.is_empty() {
return Err(ValidationError::InvalidValue(
"repodata".to_string(),
"RepodataConfig must not be empty".to_string(),
));
}
Ok(())
}
fn keys(&self) -> Vec<String> {
vec!["default".to_string(), "per-channel".to_string()]
}
#[cfg(feature = "edit")]
fn set(
&mut self,
key: &str,
value: Option<String>,
) -> Result<(), crate::config::ConfigEditError> {
if key == "repodata-config" {
*self = value
.map(|v| {
serde_json::de::from_str(&v).map_err(|e| ConfigEditError::JsonParseError {
key: key.to_string(),
source: e,
})
})
.transpose()?
.unwrap_or_default();
return Ok(());
} else if !key.starts_with("repodata-config.") {
return Err(ConfigEditError::UnknownKeyInner {
key: key.to_string(),
});
}
let subkey = key.strip_prefix("repodata-config.").unwrap();
match subkey {
"disable-bzip2" => {
self.default.disable_bzip2 = value
.map(|v| {
v.parse().map_err(|e| ConfigEditError::BoolParseError {
key: key.to_string(),
source: e,
})
})
.transpose()?;
}
"disable-zstd" => {
self.default.disable_zstd = value
.map(|v| {
v.parse().map_err(|e| ConfigEditError::BoolParseError {
key: key.to_string(),
source: e,
})
})
.transpose()?;
}
"disable-sharded" => {
self.default.disable_sharded = value
.map(|v| {
v.parse().map_err(|e| ConfigEditError::BoolParseError {
key: key.to_string(),
source: e,
})
})
.transpose()?;
}
_ => {
return Err(ConfigEditError::UnknownKeyInner {
key: key.to_string(),
});
}
}
Ok(())
}
}