git_harvest/changelog/fragment.rs
1/*********************** GNU General Public License 3.0 ***********************\
2| |
3| Copyright (C) 2026 Kevin Matthes |
4| |
5| This program is free software: you can redistribute it and/or modify |
6| it under the terms of the GNU General Public License as published by |
7| the Free Software Foundation, either version 3 of the License, or |
8| (at your option) any later version. |
9| |
10| This program is distributed in the hope that it will be useful, |
11| but WITHOUT ANY WARRANTY; without even the implied warranty of |
12| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13| GNU General Public License for more details. |
14| |
15| You should have received a copy of the GNU General Public License |
16| along with this program. If not, see <https://www.gnu.org/licenses/>. |
17| |
18\******************************************************************************/
19
20//! One harvested fragment: the changes of a single branch, not yet assembled.
21
22/// The changes harvested from one branch, written to `changelog.d/`.
23///
24/// A fragment is the pending state of one line of work. Pass two merges
25/// every fragment in `changelog.d/` into a [`crate::Section`] and deletes
26/// them. `changes` maps a bucket name to its entries.
27#[derive(
28 Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize,
29)]
30pub struct Fragment {
31 /// Link labels used by this fragment, mapped to their targets.
32 pub references: std::collections::BTreeMap<String, String>,
33
34 /// The harvested entries, keyed by bucket.
35 pub changes: std::collections::BTreeMap<String, Vec<crate::Entry>>,
36}
37
38impl Fragment {
39 /// Whether the fragment holds no entries at all.
40 pub fn is_empty(&self) -> bool {
41 self.changes.values().all(std::vec::Vec::is_empty)
42 }
43
44 /// File `entry` under `bucket`, preserving harvest order.
45 pub fn record(&mut self, bucket: &str, entry: crate::Entry) {
46 self.changes
47 .entry(bucket.to_owned())
48 .or_default()
49 .push(entry);
50 }
51
52 /// Serialise the fragment to pretty RON, indented two spaces.
53 ///
54 /// # Errors
55 ///
56 /// Returns [`sysexits::ExitCode::Software`] if the fragment cannot be
57 /// represented as RON — which should not happen for a value built by this
58 /// crate — after printing the reason to standard error.
59 pub fn to_ron(&self) -> sysexits::Result<String> {
60 let pretty = ron::ser::PrettyConfig::new().indentor(" ".to_owned());
61
62 match ron::ser::to_string_pretty(self, pretty) {
63 Ok(body) => Ok(format!("{body}\n")),
64 Err(reason) => {
65 eprintln!(
66 "git-harvest: cannot serialise the fragment: {reason}"
67 );
68 Err(sysexits::ExitCode::Software)
69 }
70 }
71 }
72}
73
74/******************************************************************************/