use serde::Deserialize;
use crate::update::UpdateConfig;
use crate::{Error, Result};
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Config {
#[serde(default)]
pub manifest_url: Option<String>,
#[serde(default)]
pub pubkeys: Vec<String>,
#[serde(default = "default_true")]
pub enabled: bool,
}
fn default_true() -> bool {
true
}
impl Config {
pub(crate) fn validate(&self) -> Result<Option<UpdateConfig>> {
if !self.enabled {
return Ok(None);
}
let url = self
.manifest_url
.as_deref()
.map(str::trim)
.unwrap_or_default();
if url.is_empty() {
return Err(Error::Config(
"`manifestUrl` is required when the plugin is enabled".into(),
));
}
if !url.starts_with("https://") && !url.starts_with("http://") {
return Err(Error::Config(format!(
"`manifestUrl` must be an http(s) URL, got {url:?}"
)));
}
if url.contains('?') {
return Err(Error::Config(
"`manifestUrl` must be a plain file URL without a query string \
(the detached signature is fetched from `<manifestUrl>.minisig`)"
.into(),
));
}
if self.pubkeys.is_empty() {
return Err(Error::Config(
"at least one trusted minisign public key is required in `pubkeys`".into(),
));
}
crate::manifest::validate_pubkeys(&self.pubkeys)?;
Ok(Some(UpdateConfig {
manifest_url: url.to_string(),
pubkeys: self.pubkeys.clone(),
}))
}
}
#[cfg(test)]
mod tests;