xtask/cmd/change_logs/
util.rs

1use 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}