Skip to main content

changepacks_java/
package.rs

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