changepacks_core/
config.rs1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
9#[serde(rename_all = "camelCase")]
10pub struct Config {
11 #[serde(default)]
13 pub ignore: Vec<String>,
14
15 #[serde(default = "default_base_branch")]
17 pub base_branch: String,
18
19 #[serde(default)]
21 pub latest_package: Option<String>,
22
23 #[serde(default)]
25 pub publish: HashMap<String, String>,
26
27 #[serde(default)]
34 pub publish_dry_run: HashMap<String, String>,
35
36 #[serde(default)]
40 pub update_on: HashMap<String, Vec<String>>,
41}
42
43fn default_base_branch() -> String {
44 "main".to_string()
45}
46
47impl Default for Config {
48 fn default() -> Self {
49 Self {
50 ignore: Vec::new(),
51 base_branch: default_base_branch(),
52 latest_package: None,
53 publish: HashMap::new(),
54 publish_dry_run: HashMap::new(),
55 update_on: HashMap::new(),
56 }
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn test_config_default() {
66 let config = Config::default();
67 assert!(config.ignore.is_empty());
68 assert_eq!(config.base_branch, "main");
69 assert!(config.latest_package.is_none());
70 assert!(config.publish.is_empty());
71 assert!(config.publish_dry_run.is_empty());
72 assert!(config.update_on.is_empty());
73 }
74
75 #[test]
76 fn test_config_publish_dry_run_map() {
77 let json = r#"{
78 "publishDryRun": {
79 "node": "npm publish --dry-run",
80 "csharp": "dotnet pack -c Release",
81 "bridge/node/package.json": "npm publish --dry-run --access public"
82 }
83 }"#;
84 let config: Config = serde_json::from_str(json).unwrap();
85 assert_eq!(config.publish_dry_run.len(), 3);
86 assert_eq!(
87 config.publish_dry_run.get("node").unwrap(),
88 "npm publish --dry-run"
89 );
90 assert_eq!(
91 config.publish_dry_run.get("csharp").unwrap(),
92 "dotnet pack -c Release"
93 );
94 assert_eq!(
95 config
96 .publish_dry_run
97 .get("bridge/node/package.json")
98 .unwrap(),
99 "npm publish --dry-run --access public"
100 );
101 }
102
103 #[test]
104 fn test_config_deserialize_full() {
105 let json = r#"{
106 "ignore": ["examples/**", "docs/**"],
107 "baseBranch": "develop",
108 "latestPackage": "crates/core/Cargo.toml",
109 "publish": {
110 "node": "npm publish --access public",
111 "rust": "cargo publish"
112 },
113 "updateOn": {
114 "crates/core/Cargo.toml": ["bridge/node/package.json", "bridge/python/pyproject.toml"]
115 }
116 }"#;
117 let config: Config = serde_json::from_str(json).unwrap();
118 assert_eq!(config.ignore, vec!["examples/**", "docs/**"]);
119 assert_eq!(config.base_branch, "develop");
120 assert_eq!(
121 config.latest_package.as_deref(),
122 Some("crates/core/Cargo.toml")
123 );
124 assert_eq!(config.publish.len(), 2);
125 assert_eq!(
126 config.publish.get("node").unwrap(),
127 "npm publish --access public"
128 );
129 assert_eq!(config.publish.get("rust").unwrap(), "cargo publish");
130 assert_eq!(config.update_on.len(), 1);
131 let update_targets = config.update_on.get("crates/core/Cargo.toml").unwrap();
132 assert_eq!(update_targets.len(), 2);
133 assert!(update_targets.contains(&"bridge/node/package.json".to_string()));
134 assert!(update_targets.contains(&"bridge/python/pyproject.toml".to_string()));
135 }
136
137 #[test]
138 fn test_config_deserialize_partial() {
139 let json = r#"{ "baseBranch": "release" }"#;
140 let config: Config = serde_json::from_str(json).unwrap();
141 assert!(config.ignore.is_empty());
142 assert_eq!(config.base_branch, "release");
143 assert!(config.latest_package.is_none());
144 assert!(config.publish.is_empty());
145 assert!(config.update_on.is_empty());
146 }
147
148 #[test]
149 fn test_config_deserialize_empty_object() {
150 let json = r#"{}"#;
151 let config: Config = serde_json::from_str(json).unwrap();
152 assert_eq!(config.base_branch, "main");
153 assert!(config.ignore.is_empty());
154 assert!(config.latest_package.is_none());
155 assert!(config.publish.is_empty());
156 assert!(config.update_on.is_empty());
157 }
158
159 #[test]
160 fn test_config_ignore_patterns() {
161 let json = r#"{ "ignore": ["**/*", "!crates/changepacks/Cargo.toml", "!bridge/**"] }"#;
162 let config: Config = serde_json::from_str(json).unwrap();
163 assert_eq!(config.ignore.len(), 3);
164 assert_eq!(config.ignore[0], "**/*");
165 assert_eq!(config.ignore[1], "!crates/changepacks/Cargo.toml");
166 assert_eq!(config.ignore[2], "!bridge/**");
167 }
168
169 #[test]
170 fn test_config_publish_map() {
171 let json = r#"{
172 "publish": {
173 "node": "npm publish",
174 "python": "uv publish",
175 "rust": "cargo publish",
176 "dart": "dart pub publish",
177 "bridge/node/package.json": "npm publish --access public"
178 }
179 }"#;
180 let config: Config = serde_json::from_str(json).unwrap();
181 assert_eq!(config.publish.len(), 5);
182 assert_eq!(config.publish.get("node").unwrap(), "npm publish");
183 assert_eq!(config.publish.get("python").unwrap(), "uv publish");
184 assert_eq!(config.publish.get("rust").unwrap(), "cargo publish");
185 assert_eq!(config.publish.get("dart").unwrap(), "dart pub publish");
186 assert_eq!(
187 config.publish.get("bridge/node/package.json").unwrap(),
188 "npm publish --access public"
189 );
190 }
191
192 #[test]
193 fn test_config_update_on_map() {
194 let json = r#"{
195 "updateOn": {
196 "crates/changepacks/Cargo.toml": ["bridge/node/package.json"],
197 "crates/core/Cargo.toml": ["bridge/python/pyproject.toml", "bridge/node/package.json"]
198 }
199 }"#;
200 let config: Config = serde_json::from_str(json).unwrap();
201 assert_eq!(config.update_on.len(), 2);
202
203 let changepacks_targets = config
204 .update_on
205 .get("crates/changepacks/Cargo.toml")
206 .unwrap();
207 assert_eq!(changepacks_targets.len(), 1);
208 assert_eq!(changepacks_targets[0], "bridge/node/package.json");
209
210 let core_targets = config.update_on.get("crates/core/Cargo.toml").unwrap();
211 assert_eq!(core_targets.len(), 2);
212 }
213
214 #[test]
215 fn test_config_serialize_roundtrip() {
216 let mut config = Config {
217 ignore: vec!["test/**".to_string()],
218 base_branch: "develop".to_string(),
219 latest_package: Some("Cargo.toml".to_string()),
220 ..Config::default()
221 };
222 config
223 .publish
224 .insert("rust".to_string(), "cargo publish".to_string());
225 config.update_on.insert(
226 "Cargo.toml".to_string(),
227 vec!["bridge/package.json".to_string()],
228 );
229
230 let json = serde_json::to_string(&config).unwrap();
231 let deserialized: Config = serde_json::from_str(&json).unwrap();
232 assert_eq!(config, deserialized);
233 }
234}