use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, JsonSchema)]
#[serde(untagged)]
pub enum VersionFileEntry {
Path(String),
Anchored(AnchoredVersionFile),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AnchoredVersionFile {
pub path: String,
#[serde(rename = "match")]
pub match_pattern: String,
}
impl VersionFileEntry {
pub fn path(&self) -> &str {
match self {
VersionFileEntry::Path(p) => p,
VersionFileEntry::Anchored(a) => &a.path,
}
}
pub fn anchor(&self) -> Option<&str> {
match self {
VersionFileEntry::Path(_) => None,
VersionFileEntry::Anchored(a) => Some(&a.match_pattern),
}
}
}
impl<'de> Deserialize<'de> for VersionFileEntry {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = serde_yaml_ng::Value::deserialize(deserializer)?;
match &value {
serde_yaml_ng::Value::String(_) => {
let path: String =
serde_yaml_ng::from_value(value).map_err(serde::de::Error::custom)?;
Ok(VersionFileEntry::Path(path))
}
serde_yaml_ng::Value::Mapping(_) => {
let anchored: AnchoredVersionFile =
serde_yaml_ng::from_value(value).map_err(serde::de::Error::custom)?;
Ok(VersionFileEntry::Anchored(anchored))
}
_ => Err(serde::de::Error::custom(
"version_files entry must be a path string or a mapping with `path` and `match`",
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_file_entry_accepts_string_and_mapping() {
let entries: Vec<VersionFileEntry> = serde_yaml_ng::from_str(
"- a.md\n- path: chart/values.yaml\n match: 'csi:.*v{version}'\n",
)
.unwrap();
assert_eq!(entries[0], VersionFileEntry::Path("a.md".to_string()));
assert_eq!(entries[0].path(), "a.md");
assert_eq!(entries[0].anchor(), None);
assert_eq!(entries[1].path(), "chart/values.yaml");
assert_eq!(entries[1].anchor(), Some("csi:.*v{version}"));
let extra_key = serde_yaml_ng::from_str::<Vec<VersionFileEntry>>(
"- path: a.md\n match: 'v{version}'\n nope: 1\n",
)
.unwrap_err()
.to_string();
assert!(extra_key.contains("nope"), "err: {extra_key}");
let wrong_kind = serde_yaml_ng::from_str::<Vec<VersionFileEntry>>("- 12\n")
.unwrap_err()
.to_string();
assert!(
wrong_kind.contains(
"version_files entry must be a path string or a mapping with `path` and `match`"
),
"err: {wrong_kind}"
);
}
#[test]
fn version_file_entry_round_trips_bare_as_a_scalar() {
let yaml =
serde_yaml_ng::to_string(&vec![VersionFileEntry::Path("a.md".to_string())]).unwrap();
assert_eq!(yaml, "- a.md\n");
let back: Vec<VersionFileEntry> = serde_yaml_ng::from_str(&yaml).unwrap();
assert_eq!(back[0], VersionFileEntry::Path("a.md".to_string()));
}
}