Skip to main content

changepacks_core/
changepack_result.rs

1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::update_type::UpdateType;
6
7/// Single changepack log entry for aggregated results.
8///
9/// Contains the update type and note from a changepack log file.
10#[derive(Debug, Serialize, Deserialize)]
11pub struct ChangePackResultLog {
12    /// Type of version update (Major, Minor, or Patch)
13    r#type: UpdateType,
14    /// User-provided changelog note
15    note: String,
16}
17
18impl ChangePackResultLog {
19    #[must_use]
20    pub const fn new(r#type: UpdateType, note: String) -> Self {
21        Self { r#type, note }
22    }
23}
24
25/// Aggregated version update results for JSON output format.
26///
27/// Contains all changepack logs applied to a project, current version, next version,
28/// and change status.
29#[derive(Debug, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct ChangePackResult {
32    /// All changepack logs applied to this project
33    logs: Vec<ChangePackResultLog>,
34    /// Current version before update
35    version: Option<String>,
36    /// New version after applying updates
37    next_version: Option<String>,
38    /// Project name from manifest
39    name: Option<String>,
40    /// Whether the project has uncommitted changes
41    changed: bool,
42    /// File path to the project manifest
43    path: PathBuf,
44}
45
46impl ChangePackResult {
47    #[must_use]
48    pub const fn new(
49        logs: Vec<ChangePackResultLog>,
50        version: Option<String>,
51        next_version: Option<String>,
52        name: Option<String>,
53        changed: bool,
54        path: PathBuf,
55    ) -> Self {
56        Self {
57            logs,
58            version,
59            next_version,
60            name,
61            changed,
62            path,
63        }
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use std::path::PathBuf;
70
71    use serde_json::Value;
72
73    use super::*;
74
75    #[test]
76    fn test_changepack_result_log_new() {
77        let log = ChangePackResultLog::new(UpdateType::Minor, "Add new API endpoint".to_string());
78        let debug_str = format!("{:?}", log);
79
80        assert!(debug_str.contains("ChangePackResultLog"));
81        assert!(debug_str.contains("Minor"));
82        assert!(debug_str.contains("Add new API endpoint"));
83    }
84
85    #[test]
86    fn test_changepack_result_log_serialize() {
87        let log = ChangePackResultLog::new(UpdateType::Patch, "Fix serialization bug".to_string());
88        let json: Value = serde_json::to_value(&log).unwrap();
89
90        assert_eq!(json.get("type"), Some(&Value::String("Patch".to_string())));
91        assert_eq!(
92            json.get("note"),
93            Some(&Value::String("Fix serialization bug".to_string()))
94        );
95        assert!(json.get("r#type").is_none());
96    }
97
98    #[test]
99    fn test_changepack_result_new() {
100        let logs = vec![ChangePackResultLog::new(
101            UpdateType::Major,
102            "Breaking changes".to_string(),
103        )];
104        let result = ChangePackResult::new(
105            logs,
106            Some("1.0.0".to_string()),
107            Some("2.0.0".to_string()),
108            Some("changepacks-core".to_string()),
109            true,
110            PathBuf::from("crates/core/Cargo.toml"),
111        );
112        let debug_str = format!("{:?}", result);
113
114        assert!(debug_str.contains("ChangePackResult"));
115        assert!(debug_str.contains("1.0.0"));
116        assert!(debug_str.contains("2.0.0"));
117        assert!(debug_str.contains("changepacks-core"));
118        assert!(debug_str.contains("changed: true"));
119        assert!(debug_str.contains("crates/core/Cargo.toml"));
120    }
121
122    #[test]
123    fn test_changepack_result_serialize_camel_case() {
124        let logs = vec![ChangePackResultLog::new(
125            UpdateType::Minor,
126            "Add feature".to_string(),
127        )];
128        let result = ChangePackResult::new(
129            logs,
130            Some("1.1.0".to_string()),
131            Some("1.2.0".to_string()),
132            Some("core".to_string()),
133            true,
134            PathBuf::from("crates/core/Cargo.toml"),
135        );
136        let json: Value = serde_json::to_value(&result).unwrap();
137
138        assert!(json.get("logs").is_some());
139        assert!(json.get("version").is_some());
140        assert!(json.get("nextVersion").is_some());
141        assert!(json.get("name").is_some());
142        assert!(json.get("changed").is_some());
143        assert!(json.get("path").is_some());
144        assert!(json.get("next_version").is_none());
145    }
146
147    #[test]
148    fn test_changepack_result_deserialize_roundtrip() {
149        let logs = vec![
150            ChangePackResultLog::new(UpdateType::Major, "Breaking release".to_string()),
151            ChangePackResultLog::new(UpdateType::Patch, "Hotfix".to_string()),
152        ];
153        let result = ChangePackResult::new(
154            logs,
155            Some("1.0.0".to_string()),
156            Some("2.0.1".to_string()),
157            Some("core".to_string()),
158            false,
159            PathBuf::from("crates/core/Cargo.toml"),
160        );
161
162        let json = serde_json::to_string(&result).unwrap();
163        let deserialized: ChangePackResult = serde_json::from_str(&json).unwrap();
164
165        let original_value = serde_json::to_value(&result).unwrap();
166        let deserialized_value = serde_json::to_value(&deserialized).unwrap();
167        assert_eq!(deserialized_value, original_value);
168    }
169
170    #[test]
171    fn test_changepack_result_with_empty_logs() {
172        let result = ChangePackResult::new(
173            Vec::new(),
174            Some("1.0.0".to_string()),
175            Some("1.0.1".to_string()),
176            Some("core".to_string()),
177            true,
178            PathBuf::from("crates/core/Cargo.toml"),
179        );
180        let debug_str = format!("{:?}", result);
181        let json: Value = serde_json::to_value(&result).unwrap();
182
183        assert!(debug_str.contains("logs: []"));
184        assert!(json.get("logs").unwrap().as_array().unwrap().is_empty());
185    }
186
187    #[test]
188    fn test_changepack_result_with_none_fields() {
189        let logs = vec![ChangePackResultLog::new(
190            UpdateType::Patch,
191            "No version bump metadata".to_string(),
192        )];
193        let result = ChangePackResult::new(
194            logs,
195            None,
196            None,
197            None,
198            false,
199            PathBuf::from("crates/core/Cargo.toml"),
200        );
201        let json: Value = serde_json::to_value(&result).unwrap();
202
203        assert!(json.get("version").unwrap().is_null());
204        assert!(json.get("nextVersion").unwrap().is_null());
205        assert!(json.get("name").unwrap().is_null());
206        assert_eq!(json.get("changed"), Some(&Value::Bool(false)));
207    }
208}