#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum ChangeKind {
None,
Compatible,
Breaking,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
pub(crate) struct ChangeEntry {
pub(crate) unit: String,
pub(crate) kind: ChangeKind,
pub(crate) summary: String,
}
#[derive(Debug, Clone, serde::Deserialize)]
pub(crate) struct FragmentDoc {
#[serde(rename = "change")]
pub entries: Vec<ChangeEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ChangeFragmentFile {
pub(crate) file_name: String,
pub(crate) entries: Vec<ChangeEntry>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_single_entry() {
let toml = "\
[[change]]
unit = \"arcature-auth\"
kind = \"compatible\"
summary = \"Add refresh-session support\"
";
let doc: FragmentDoc = toml::from_str(toml).expect("parses");
assert_eq!(doc.entries.len(), 1);
assert_eq!(doc.entries[0].unit, "arcature-auth");
assert_eq!(doc.entries[0].kind, ChangeKind::Compatible);
assert_eq!(doc.entries[0].summary, "Add refresh-session support");
}
#[test]
fn parses_multiple_entries_in_one_file() {
let toml = "\
[[change]]
unit = \"arcature-auth\"
kind = \"breaking\"
summary = \"Replace AuthUser identity contract\"
[[change]]
unit = \"arcature-db\"
kind = \"compatible\"
summary = \"Add connection-pool stats\"
";
let doc: FragmentDoc = toml::from_str(toml).expect("parses");
assert_eq!(doc.entries.len(), 2);
assert_eq!(doc.entries[0].kind, ChangeKind::Breaking);
assert_eq!(doc.entries[1].kind, ChangeKind::Compatible);
}
#[test]
fn parses_none_kind() {
let toml = "\
[[change]]
unit = \"arcature-auth\"
kind = \"none\"
summary = \"Internal refactor, no public API change\"
";
let doc: FragmentDoc = toml::from_str(toml).expect("parses");
assert_eq!(doc.entries[0].kind, ChangeKind::None);
}
#[test]
fn rejects_unknown_kind() {
let toml = "\
[[change]]
unit = \"arcature-auth\"
kind = \"major\"
summary = \"x\"
";
let err = toml::from_str::<FragmentDoc>(toml).expect_err("unknown kind must fail");
assert!(err.to_string().contains("major"));
}
#[test]
fn rejects_missing_field() {
let toml = "\
[[change]]
unit = \"arcature-auth\"
kind = \"compatible\"
";
let err = toml::from_str::<FragmentDoc>(toml).expect_err("missing summary must fail");
assert!(err.to_string().contains("summary"));
}
}