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 default_publish_command(&self) -> String {
96 "./gradlew publish".to_string()
97 }
98
99 fn dependencies(&self) -> &HashSet<String> {
100 &self.dependencies
101 }
102
103 fn add_dependency(&mut self, dependency: &str) {
104 self.dependencies.insert(dependency.to_string());
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use changepacks_core::UpdateType;
112 use std::fs;
113 use tempfile::TempDir;
114 use tokio::fs::read_to_string;
115
116 #[tokio::test]
117 async fn test_gradle_package_new() {
118 let package = GradlePackage::new(
119 Some("test-package".to_string()),
120 Some("1.0.0".to_string()),
121 PathBuf::from("/test/build.gradle.kts"),
122 PathBuf::from("test/build.gradle.kts"),
123 );
124
125 assert_eq!(package.name(), Some("test-package"));
126 assert_eq!(package.version(), Some("1.0.0"));
127 assert_eq!(package.path(), PathBuf::from("/test/build.gradle.kts"));
128 assert_eq!(
129 package.relative_path(),
130 PathBuf::from("test/build.gradle.kts")
131 );
132 assert_eq!(package.language(), Language::Java);
133 assert!(!package.is_changed());
134 assert_eq!(package.default_publish_command(), "./gradlew publish");
135 }
136
137 #[tokio::test]
138 async fn test_gradle_package_set_changed() {
139 let mut package = GradlePackage::new(
140 Some("test-package".to_string()),
141 Some("1.0.0".to_string()),
142 PathBuf::from("/test/build.gradle.kts"),
143 PathBuf::from("test/build.gradle.kts"),
144 );
145
146 assert!(!package.is_changed());
147 package.set_changed(true);
148 assert!(package.is_changed());
149 package.set_changed(false);
150 assert!(!package.is_changed());
151 }
152
153 #[tokio::test]
154 async fn test_gradle_package_update_version_kts_patch() {
155 let temp_dir = TempDir::new().unwrap();
156 let project_dir = temp_dir.path().join("myproject");
157 fs::create_dir_all(&project_dir).unwrap();
158
159 let build_gradle = project_dir.join("build.gradle.kts");
160 fs::write(
161 &build_gradle,
162 r#"
163plugins {
164 id("java")
165}
166
167group = "com.example"
168version = "1.0.0"
169"#,
170 )
171 .unwrap();
172
173 let mut package = GradlePackage::new(
174 Some("myproject".to_string()),
175 Some("1.0.0".to_string()),
176 build_gradle.clone(),
177 PathBuf::from("myproject/build.gradle.kts"),
178 );
179
180 package.update_version(UpdateType::Patch).await.unwrap();
181
182 let content = read_to_string(&build_gradle).await.unwrap();
183 assert!(content.contains(r#"version = "1.0.1""#));
184
185 temp_dir.close().unwrap();
186 }
187
188 #[tokio::test]
189 async fn test_gradle_package_update_version_kts_minor() {
190 let temp_dir = TempDir::new().unwrap();
191 let project_dir = temp_dir.path().join("myproject");
192 fs::create_dir_all(&project_dir).unwrap();
193
194 let build_gradle = project_dir.join("build.gradle.kts");
195 fs::write(
196 &build_gradle,
197 r#"
198plugins {
199 id("java")
200}
201
202group = "com.example"
203version = "1.0.0"
204"#,
205 )
206 .unwrap();
207
208 let mut package = GradlePackage::new(
209 Some("myproject".to_string()),
210 Some("1.0.0".to_string()),
211 build_gradle.clone(),
212 PathBuf::from("myproject/build.gradle.kts"),
213 );
214
215 package.update_version(UpdateType::Minor).await.unwrap();
216
217 let content = read_to_string(&build_gradle).await.unwrap();
218 assert!(content.contains(r#"version = "1.1.0""#));
219
220 temp_dir.close().unwrap();
221 }
222
223 #[tokio::test]
224 async fn test_gradle_package_update_version_kts_major() {
225 let temp_dir = TempDir::new().unwrap();
226 let project_dir = temp_dir.path().join("myproject");
227 fs::create_dir_all(&project_dir).unwrap();
228
229 let build_gradle = project_dir.join("build.gradle.kts");
230 fs::write(
231 &build_gradle,
232 r#"
233plugins {
234 id("java")
235}
236
237group = "com.example"
238version = "1.0.0"
239"#,
240 )
241 .unwrap();
242
243 let mut package = GradlePackage::new(
244 Some("myproject".to_string()),
245 Some("1.0.0".to_string()),
246 build_gradle.clone(),
247 PathBuf::from("myproject/build.gradle.kts"),
248 );
249
250 package.update_version(UpdateType::Major).await.unwrap();
251
252 let content = read_to_string(&build_gradle).await.unwrap();
253 assert!(content.contains(r#"version = "2.0.0""#));
254
255 temp_dir.close().unwrap();
256 }
257
258 #[tokio::test]
259 async fn test_gradle_package_update_version_groovy() {
260 let temp_dir = TempDir::new().unwrap();
261 let project_dir = temp_dir.path().join("myproject");
262 fs::create_dir_all(&project_dir).unwrap();
263
264 let build_gradle = project_dir.join("build.gradle");
265 fs::write(
266 &build_gradle,
267 r#"
268plugins {
269 id 'java'
270}
271
272group = 'com.example'
273version = '1.0.0'
274"#,
275 )
276 .unwrap();
277
278 let mut package = GradlePackage::new(
279 Some("myproject".to_string()),
280 Some("1.0.0".to_string()),
281 build_gradle.clone(),
282 PathBuf::from("myproject/build.gradle"),
283 );
284
285 package.update_version(UpdateType::Patch).await.unwrap();
286
287 let content = read_to_string(&build_gradle).await.unwrap();
288 assert!(content.contains("version = '1.0.1'"));
289
290 temp_dir.close().unwrap();
291 }
292
293 #[tokio::test]
294 async fn test_gradle_package_update_version_with_fallback() {
295 let temp_dir = TempDir::new().unwrap();
296 let project_dir = temp_dir.path().join("myproject");
297 fs::create_dir_all(&project_dir).unwrap();
298
299 let build_gradle = project_dir.join("build.gradle.kts");
300 fs::write(
301 &build_gradle,
302 r#"
303group = "com.devfive"
304version = project.findProperty("releaseVersion") ?: "1.0.11"
305"#,
306 )
307 .unwrap();
308
309 let mut package = GradlePackage::new(
310 Some("myproject".to_string()),
311 Some("1.0.11".to_string()),
312 build_gradle.clone(),
313 PathBuf::from("myproject/build.gradle.kts"),
314 );
315
316 package.update_version(UpdateType::Patch).await.unwrap();
317
318 let content = read_to_string(&build_gradle).await.unwrap();
319 assert!(content.contains(r#"?: "1.0.12""#));
320
321 temp_dir.close().unwrap();
322 }
323
324 #[test]
325 fn test_gradle_package_dependencies() {
326 let mut package = GradlePackage::new(
327 Some("test-package".to_string()),
328 Some("1.0.0".to_string()),
329 PathBuf::from("/test/build.gradle.kts"),
330 PathBuf::from("test/build.gradle.kts"),
331 );
332
333 assert!(package.dependencies().is_empty());
335
336 package.add_dependency("core");
338 package.add_dependency("utils");
339
340 let deps = package.dependencies();
341 assert_eq!(deps.len(), 2);
342 assert!(deps.contains("core"));
343 assert!(deps.contains("utils"));
344
345 package.add_dependency("core");
347 assert_eq!(package.dependencies().len(), 2);
348 }
349}