changepacks_java/
package.rs1use anyhow::Result;
2use async_trait::async_trait;
3use changepacks_core::{Language, Package, UpdateType};
4use changepacks_utils::next_version;
5use std::collections::HashSet;
6use std::path::{Path, PathBuf};
7use tokio::fs::{read_to_string, write};
8
9use crate::{update_version_in_groovy, update_version_in_kts};
10
11#[derive(Debug)]
12pub struct GradlePackage {
13 name: Option<String>,
14 version: Option<String>,
15 path: PathBuf,
16 relative_path: PathBuf,
17 is_changed: bool,
18 dependencies: HashSet<String>,
19}
20
21impl GradlePackage {
22 #[must_use]
23 pub fn new(
24 name: Option<String>,
25 version: Option<String>,
26 path: PathBuf,
27 relative_path: PathBuf,
28 ) -> Self {
29 Self {
30 name,
31 version,
32 path,
33 relative_path,
34 is_changed: false,
35 dependencies: HashSet::new(),
36 }
37 }
38}
39
40#[async_trait]
41impl Package for GradlePackage {
42 fn name(&self) -> Option<&str> {
43 self.name.as_deref()
44 }
45
46 fn version(&self) -> Option<&str> {
47 self.version.as_deref()
48 }
49
50 fn path(&self) -> &Path {
51 &self.path
52 }
53
54 fn relative_path(&self) -> &Path {
55 &self.relative_path
56 }
57
58 async fn update_version(&mut self, update_type: UpdateType) -> Result<()> {
59 let current_version = self.version.as_deref().unwrap_or("0.0.0");
60 let new_version = next_version(current_version, update_type)?;
61
62 let content = read_to_string(&self.path).await?;
63 let file_name = self
64 .path
65 .file_name()
66 .and_then(|f| f.to_str())
67 .unwrap_or_default();
68 let is_kts = Path::new(file_name)
69 .extension()
70 .is_some_and(|ext| ext.eq_ignore_ascii_case("kts"));
71
72 let updated_content = if is_kts {
73 update_version_in_kts(&content, &new_version)
74 } else {
75 update_version_in_groovy(&content, &new_version)
76 };
77
78 write(&self.path, updated_content).await?;
79 self.version = Some(new_version);
80 Ok(())
81 }
82
83 fn language(&self) -> Language {
84 Language::Java
85 }
86
87 fn set_changed(&mut self, changed: bool) {
88 self.is_changed = changed;
89 }
90
91 fn is_changed(&self) -> bool {
92 self.is_changed
93 }
94
95 fn set_name(&mut self, name: String) {
96 self.name = Some(name);
97 }
98
99 fn default_publish_command(&self) -> String {
100 "./gradlew publish".to_string()
101 }
102
103 fn dependencies(&self) -> &HashSet<String> {
104 &self.dependencies
105 }
106
107 fn add_dependency(&mut self, dependency: &str) {
108 self.dependencies.insert(dependency.to_string());
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use changepacks_core::UpdateType;
116 use std::fs;
117 use tempfile::TempDir;
118 use tokio::fs::read_to_string;
119
120 #[tokio::test]
121 async fn test_gradle_package_new() {
122 let package = GradlePackage::new(
123 Some("test-package".to_string()),
124 Some("1.0.0".to_string()),
125 PathBuf::from("/test/build.gradle.kts"),
126 PathBuf::from("test/build.gradle.kts"),
127 );
128
129 assert_eq!(package.name(), Some("test-package"));
130 assert_eq!(package.version(), Some("1.0.0"));
131 assert_eq!(package.path(), PathBuf::from("/test/build.gradle.kts"));
132 assert_eq!(
133 package.relative_path(),
134 PathBuf::from("test/build.gradle.kts")
135 );
136 assert_eq!(package.language(), Language::Java);
137 assert!(!package.is_changed());
138 assert_eq!(package.default_publish_command(), "./gradlew publish");
139 }
140
141 #[tokio::test]
142 async fn test_gradle_package_set_changed() {
143 let mut package = GradlePackage::new(
144 Some("test-package".to_string()),
145 Some("1.0.0".to_string()),
146 PathBuf::from("/test/build.gradle.kts"),
147 PathBuf::from("test/build.gradle.kts"),
148 );
149
150 assert!(!package.is_changed());
151 package.set_changed(true);
152 assert!(package.is_changed());
153 package.set_changed(false);
154 assert!(!package.is_changed());
155 }
156
157 #[tokio::test]
158 async fn test_gradle_package_update_version_kts_patch() {
159 let temp_dir = TempDir::new().unwrap();
160 let project_dir = temp_dir.path().join("myproject");
161 fs::create_dir_all(&project_dir).unwrap();
162
163 let build_gradle = project_dir.join("build.gradle.kts");
164 fs::write(
165 &build_gradle,
166 r#"
167plugins {
168 id("java")
169}
170
171group = "com.example"
172version = "1.0.0"
173"#,
174 )
175 .unwrap();
176
177 let mut package = GradlePackage::new(
178 Some("myproject".to_string()),
179 Some("1.0.0".to_string()),
180 build_gradle.clone(),
181 PathBuf::from("myproject/build.gradle.kts"),
182 );
183
184 package.update_version(UpdateType::Patch).await.unwrap();
185
186 let content = read_to_string(&build_gradle).await.unwrap();
187 assert!(content.contains(r#"version = "1.0.1""#));
188
189 temp_dir.close().unwrap();
190 }
191
192 #[tokio::test]
193 async fn test_gradle_package_update_version_kts_minor() {
194 let temp_dir = TempDir::new().unwrap();
195 let project_dir = temp_dir.path().join("myproject");
196 fs::create_dir_all(&project_dir).unwrap();
197
198 let build_gradle = project_dir.join("build.gradle.kts");
199 fs::write(
200 &build_gradle,
201 r#"
202plugins {
203 id("java")
204}
205
206group = "com.example"
207version = "1.0.0"
208"#,
209 )
210 .unwrap();
211
212 let mut package = GradlePackage::new(
213 Some("myproject".to_string()),
214 Some("1.0.0".to_string()),
215 build_gradle.clone(),
216 PathBuf::from("myproject/build.gradle.kts"),
217 );
218
219 package.update_version(UpdateType::Minor).await.unwrap();
220
221 let content = read_to_string(&build_gradle).await.unwrap();
222 assert!(content.contains(r#"version = "1.1.0""#));
223
224 temp_dir.close().unwrap();
225 }
226
227 #[tokio::test]
228 async fn test_gradle_package_update_version_kts_major() {
229 let temp_dir = TempDir::new().unwrap();
230 let project_dir = temp_dir.path().join("myproject");
231 fs::create_dir_all(&project_dir).unwrap();
232
233 let build_gradle = project_dir.join("build.gradle.kts");
234 fs::write(
235 &build_gradle,
236 r#"
237plugins {
238 id("java")
239}
240
241group = "com.example"
242version = "1.0.0"
243"#,
244 )
245 .unwrap();
246
247 let mut package = GradlePackage::new(
248 Some("myproject".to_string()),
249 Some("1.0.0".to_string()),
250 build_gradle.clone(),
251 PathBuf::from("myproject/build.gradle.kts"),
252 );
253
254 package.update_version(UpdateType::Major).await.unwrap();
255
256 let content = read_to_string(&build_gradle).await.unwrap();
257 assert!(content.contains(r#"version = "2.0.0""#));
258
259 temp_dir.close().unwrap();
260 }
261
262 #[tokio::test]
263 async fn test_gradle_package_update_version_groovy() {
264 let temp_dir = TempDir::new().unwrap();
265 let project_dir = temp_dir.path().join("myproject");
266 fs::create_dir_all(&project_dir).unwrap();
267
268 let build_gradle = project_dir.join("build.gradle");
269 fs::write(
270 &build_gradle,
271 r#"
272plugins {
273 id 'java'
274}
275
276group = 'com.example'
277version = '1.0.0'
278"#,
279 )
280 .unwrap();
281
282 let mut package = GradlePackage::new(
283 Some("myproject".to_string()),
284 Some("1.0.0".to_string()),
285 build_gradle.clone(),
286 PathBuf::from("myproject/build.gradle"),
287 );
288
289 package.update_version(UpdateType::Patch).await.unwrap();
290
291 let content = read_to_string(&build_gradle).await.unwrap();
292 assert!(content.contains("version = '1.0.1'"));
293
294 temp_dir.close().unwrap();
295 }
296
297 #[tokio::test]
298 async fn test_gradle_package_update_version_with_fallback() {
299 let temp_dir = TempDir::new().unwrap();
300 let project_dir = temp_dir.path().join("myproject");
301 fs::create_dir_all(&project_dir).unwrap();
302
303 let build_gradle = project_dir.join("build.gradle.kts");
304 fs::write(
305 &build_gradle,
306 r#"
307group = "com.devfive"
308version = project.findProperty("releaseVersion") ?: "1.0.11"
309"#,
310 )
311 .unwrap();
312
313 let mut package = GradlePackage::new(
314 Some("myproject".to_string()),
315 Some("1.0.11".to_string()),
316 build_gradle.clone(),
317 PathBuf::from("myproject/build.gradle.kts"),
318 );
319
320 package.update_version(UpdateType::Patch).await.unwrap();
321
322 let content = read_to_string(&build_gradle).await.unwrap();
323 assert!(content.contains(r#"?: "1.0.12""#));
324
325 temp_dir.close().unwrap();
326 }
327
328 #[test]
329 fn test_gradle_package_dependencies() {
330 let mut package = GradlePackage::new(
331 Some("test-package".to_string()),
332 Some("1.0.0".to_string()),
333 PathBuf::from("/test/build.gradle.kts"),
334 PathBuf::from("test/build.gradle.kts"),
335 );
336
337 assert!(package.dependencies().is_empty());
339
340 package.add_dependency("core");
342 package.add_dependency("utils");
343
344 let deps = package.dependencies();
345 assert_eq!(deps.len(), 2);
346 assert!(deps.contains("core"));
347 assert!(deps.contains("utils"));
348
349 package.add_dependency("core");
351 assert_eq!(package.dependencies().len(), 2);
352 }
353
354 #[test]
355 fn test_set_name() {
356 let mut package = GradlePackage::new(
357 None,
358 Some("1.0.0".to_string()),
359 PathBuf::from("/test/build.gradle.kts"),
360 PathBuf::from("build.gradle.kts"),
361 );
362 assert_eq!(package.name(), None);
363 package.set_name("my-project".to_string());
364 assert_eq!(package.name(), Some("my-project"));
365 }
366}