xtask/cmd/change_logs/
util.rs1use std::path::{Path, PathBuf};
2
3use anyhow::Context;
4use serde::Deserialize as _;
5use serde::de::IntoDeserializer;
6use serde_derive::Deserialize;
7
8#[derive(Debug, Clone)]
9pub struct Fragment {
10 pub path: PathBuf,
11 pub pr_number: u64,
12 pub toml: toml_edit::DocumentMut,
13 pub changed: bool,
14 pub deleted: bool,
15}
16
17impl Fragment {
18 pub fn new(pr_number: u64, path: &Path) -> anyhow::Result<Self> {
19 let content = std::fs::read_to_string(path).context("change log not found")?;
20 Ok(Fragment {
21 pr_number,
22 path: path.to_path_buf(),
23 toml: content
24 .parse::<toml_edit::DocumentMut>()
25 .context("change log is not valid toml")?,
26 changed: false,
27 deleted: false,
28 })
29 }
30}
31
32#[derive(Debug, Clone, Deserialize)]
33#[serde(deny_unknown_fields)]
34pub struct PackageChangeLog {
35 #[serde(skip, default)]
36 pub pr_number: u64,
37 #[serde(alias = "cat")]
38 pub category: String,
39 #[serde(alias = "desc")]
40 pub description: String,
41 #[serde(default)]
42 #[serde(alias = "author")]
43 pub authors: Vec<String>,
44 #[serde(default)]
45 #[serde(alias = "break", alias = "major")]
46 pub breaking: bool,
47}
48
49impl Fragment {
50 pub fn remove_package(&mut self, package: &str) -> anyhow::Result<Vec<PackageChangeLog>> {
51 let Some(items) = self.toml.remove(package) else {
52 return Ok(Vec::new());
53 };
54
55 self.changed = true;
56
57 package_to_logs(self.pr_number, items)
58 }
59
60 pub fn packages(&self) -> impl IntoIterator<Item = (&str, &toml_edit::Item)> {
61 self.toml.as_table()
62 }
63
64 pub fn save(&mut self) -> anyhow::Result<()> {
65 if !self.changed {
66 return Ok(());
67 }
68
69 if self.toml.is_empty() {
70 std::fs::remove_file(&self.path).context("remove")?;
71 self.deleted = true;
72 } else {
73 std::fs::write(&self.path, self.toml.to_string()).context("write")?;
74 }
75
76 self.changed = false;
77
78 Ok(())
79 }
80}
81
82pub fn package_to_logs(pr_number: u64, items: toml_edit::Item) -> anyhow::Result<Vec<PackageChangeLog>> {
83 let value = items.into_value().expect("items must be a value").into_deserializer();
84 let mut logs = Vec::<PackageChangeLog>::deserialize(value).context("deserialize")?;
85
86 logs.iter_mut().for_each(|log| {
87 log.pr_number = pr_number;
88 });
89
90 Ok(logs)
91}