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