1use std::collections::{HashMap, HashSet};
2
3use crate::changeset::Changeset;
4use crate::package::PackageGraph;
5use crate::version::{VersionEditor, VersionMod};
6
7#[derive(Debug, Default)]
8pub struct Bump<T> {
9 changesets: Vec<Changeset<T>>,
10 package_update: HashMap<String, VersionMod<T>>,
11 package_changesets: HashMap<String, HashSet<usize>>,
12}
13
14impl<'a, T: VersionEditor> Bump<T> {
15 fn set_package_update(
16 package_update: &mut HashMap<String, VersionMod<T>>,
17 name: &str,
18 version: VersionMod<T>,
19 ) {
20 if let Some(concat_version) = package_update.get_mut(name) {
21 if &version > concat_version {
22 *concat_version = version;
23 }
24 } else {
25 package_update.insert(name.to_owned(), version);
26 }
27 }
28
29 pub fn add(&mut self, changeset: Changeset<T>, graph: &PackageGraph<'_, T>) {
30 let min_update = T::options().into_iter().min().unwrap_or_default();
31 let index = self.changesets.len();
32 self.changesets.insert(index, changeset);
33 let changeset = &self.changesets[index];
34
35 for (name, version) in &changeset.packages {
36 if let Some(changesets) = self.package_changesets.get_mut(name) {
37 changesets.insert(index);
38 } else {
39 self
40 .package_changesets
41 .insert(name.clone(), vec![index].into_iter().collect());
42 }
43
44 Self::set_package_update(&mut self.package_update, name, version.clone());
45
46 for package in graph.child_changes(name) {
47 Self::set_package_update(
48 &mut self.package_update,
49 &package.name,
50 VersionMod::new(min_update.clone()),
51 );
52 }
53 }
54 }
55
56 pub fn is_empty(&self) -> bool {
57 self.changesets.is_empty()
58 }
59
60 pub fn package(&'a self, name: &'a str) -> PackageBump<'a, T> {
61 PackageBump { name, bump: self }
62 }
63}
64
65pub struct PackageBump<'a, T> {
66 name: &'a str,
67 bump: &'a Bump<T>,
68}
69
70impl<'a, T> PackageBump<'a, T> {
71 pub fn changesets(&self) -> Option<Vec<&'a Changeset<T>>> {
72 self.bump.package_changesets.get(self.name).map(|indexes| {
73 indexes
74 .iter()
75 .map(|index| &self.bump.changesets[*index])
76 .collect()
77 })
78 }
79
80 pub fn name(&self) -> &str {
81 self.name
82 }
83
84 pub fn version(&self) -> Option<&VersionMod<T>> {
85 self.bump.package_update.get(self.name)
86 }
87}
88
89#[cfg(test)]
90mod tests {
91
92 use super::*;
93 use crate::prelude::*;
94
95 #[test]
96 fn add() {
97 let packages = vec![];
98
99 let mut bump = Bump::default();
100 let graph = packages.as_package_graph();
101
102 bump.add(
103 Changeset {
104 packages: vec![("mol".to_owned(), VersionMod::new(Semantic::minor()))]
105 .into_iter()
106 .collect(),
107 message: "Hi".to_owned(),
108 },
109 &graph,
110 );
111 bump.add(
112 Changeset {
113 packages: vec![
114 ("mol".to_owned(), VersionMod::new(Semantic::patch())),
115 ("mol-core".to_owned(), VersionMod::new(Semantic::major())),
116 ]
117 .into_iter()
118 .collect(),
119 message: "Too bad we dont play games".to_owned(),
120 },
121 &graph,
122 );
123
124 assert_eq!(bump.changesets.len(), 2);
125 assert_eq!(
126 bump.package_changesets.get("mol-core"),
127 Some(&vec![1].into_iter().collect())
128 );
129 assert_eq!(
130 bump.package_changesets.get("mol"),
131 Some(&vec![0, 1].into_iter().collect())
132 );
133 assert_eq!(
134 bump.package_update.get("mol"),
135 Some(&VersionMod::new(Semantic::minor()))
136 );
137 assert_eq!(
138 bump.package_update.get("mol-core"),
139 Some(&VersionMod::new(Semantic::major()))
140 );
141 }
142
143 #[test]
144 fn changesets() {
145 let packages = vec![];
146
147 let mut bump = Bump::default();
148 let graph = packages.as_package_graph();
149
150 bump.add(
151 Changeset {
152 packages: vec![("mol".to_owned(), VersionMod::new(Semantic::minor()))]
153 .into_iter()
154 .collect(),
155 message: "Hi".to_owned(),
156 },
157 &graph,
158 );
159 bump.add(
160 Changeset {
161 packages: vec![("mol-core".to_owned(), VersionMod::new(Semantic::major()))]
162 .into_iter()
163 .collect(),
164 message: "Too bad we dont play games".to_owned(),
165 },
166 &graph,
167 );
168
169 let changesets = bump.package("mol").changesets();
170
171 assert!(changesets.is_some());
172
173 let changesets = changesets.unwrap();
174
175 assert_eq!(changesets.len(), 1);
176 assert_eq!(changesets[0].message, "Hi");
177
178 let changesets = bump.package("mol-core").changesets();
179
180 assert!(changesets.is_some());
181
182 let changesets = changesets.unwrap();
183
184 assert_eq!(changesets.len(), 1);
185 assert_eq!(changesets[0].message, "Too bad we dont play games");
186 }
187
188 #[test]
189 fn dependecy_bump() {
190 let packages = vec![
191 Package {
192 name: "mol-core".to_owned(),
193 path: "".into(),
194 version: "0.1.0".into(),
195 dependencies: vec![],
196 },
197 Package {
198 name: "mol".to_owned(),
199 path: "".into(),
200 version: "0.1.0".into(),
201 dependencies: vec![("mol-core".to_owned(), "0.1".to_owned())],
202 },
203 Package {
204 name: "mol-cargo".to_owned(),
205 path: "".into(),
206 version: "0.1.0".into(),
207 dependencies: vec![("mol-core".to_owned(), "0.1".to_owned())],
208 },
209 ];
210
211 let mut bump = Bump::default();
212 let graph = packages.as_package_graph();
213
214 bump.add(
215 Changeset {
216 packages: vec![("mol-core".to_owned(), VersionMod::new(Semantic::minor()))]
217 .into_iter()
218 .collect(),
219 message: "Too bad we dont play games".to_owned(),
220 },
221 &graph,
222 );
223
224 assert_eq!(
225 bump.package_update,
226 vec![
227 ("mol-core".to_owned(), VersionMod::new(Semantic::minor())),
228 ("mol".to_owned(), VersionMod::new(Semantic::patch())),
229 ("mol-cargo".to_owned(), VersionMod::new(Semantic::patch()))
230 ]
231 .into_iter()
232 .collect()
233 );
234 }
235}