xtask/cmd/change_logs/
util.rs

1use std::path::{Path, PathBuf};
2
3use anyhow::Context;
4use serde::Deserialize;
5use serde::de::IntoDeserializer;
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::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}