|
5 | 5 |
|
6 | 6 | use crate::config::{RailConfig, UnifyConfig, WorkspaceConfig}; |
7 | 7 | use crate::error::{RailError, RailResult}; |
| 8 | +use crate::toml::builder::RailConfigBuilder; |
8 | 9 | use crate::workspace::WorkspaceContext; |
9 | 10 | use std::fs; |
10 | 11 | use std::io::{self, Write}; |
@@ -69,8 +70,14 @@ pub fn run_init( |
69 | 70 | // 4. Build config |
70 | 71 | let config = build_rail_config(workspace_root.to_path_buf(), unify); |
71 | 72 |
|
72 | | - // 6. Serialize with comments |
73 | | - let toml_content = serialize_config_with_comments(&config)?; |
| 73 | + // 6. Serialize with comments using Builder |
| 74 | + let toml_content = RailConfigBuilder::new() |
| 75 | + .header() |
| 76 | + .workspace(&config.workspace.root) |
| 77 | + .unify(&config.unify) |
| 78 | + .release(&config.release) |
| 79 | + .splits_template() |
| 80 | + .build()?; |
74 | 81 |
|
75 | 82 | // 5. Write or print |
76 | 83 | if dry_run { |
@@ -112,178 +119,10 @@ fn build_rail_config(_workspace_root: PathBuf, unify: UnifyConfig) -> RailConfig |
112 | 119 | unify, |
113 | 120 | release: crate::config::ReleaseConfig::default(), |
114 | 121 | splits: vec![], // Empty by default - use 'cargo rail split init' to configure |
| 122 | + formatting: crate::config::FormattingConfig::default(), |
115 | 123 | } |
116 | 124 | } |
117 | 125 |
|
118 | | -/// Serialize RailConfig to TOML string with minimal, clean comments |
119 | | -fn serialize_config_with_comments(config: &RailConfig) -> RailResult<String> { |
120 | | - let mut output = String::new(); |
121 | | - |
122 | | - // Header |
123 | | - output.push_str("# cargo-rail configuration\n"); |
124 | | - output.push_str("# Generated by: cargo rail init\n"); |
125 | | - output.push_str("# Documentation: https://github.com/loadingalias/cargo-rail\n\n"); |
126 | | - |
127 | | - // Workspace |
128 | | - output.push_str("[workspace]\n"); |
129 | | - output.push_str(&format!("root = \"{}\"\n\n", config.workspace.root.display())); |
130 | | - |
131 | | - // Dependency Unification |
132 | | - output.push_str("[unify]\n"); |
133 | | - output.push_str(&format!("use_all_features = {}\n", config.unify.use_all_features)); |
134 | | - output.push_str(&format!("allow_renamed = {}\n", config.unify.allow_renamed)); |
135 | | - |
136 | | - if !config.unify.exclude.is_empty() { |
137 | | - output.push_str("exclude = ["); |
138 | | - for (i, dep) in config.unify.exclude.iter().enumerate() { |
139 | | - if i > 0 { |
140 | | - output.push_str(", "); |
141 | | - } |
142 | | - output.push_str(&format!("\"{}\"", dep)); |
143 | | - } |
144 | | - output.push_str("]\n"); |
145 | | - } else { |
146 | | - output.push_str("exclude = [] # Dependencies to skip unification\n"); |
147 | | - } |
148 | | - |
149 | | - if !config.unify.include.is_empty() { |
150 | | - output.push_str("include = ["); |
151 | | - for (i, dep) in config.unify.include.iter().enumerate() { |
152 | | - if i > 0 { |
153 | | - output.push_str(", "); |
154 | | - } |
155 | | - output.push_str(&format!("\"{}\"", dep)); |
156 | | - } |
157 | | - output.push_str("]\n"); |
158 | | - } else { |
159 | | - output.push_str("include = [] # Dependencies to force unification\n"); |
160 | | - } |
161 | | - |
162 | | - output.push('\n'); |
163 | | - |
164 | | - // Conflict handling |
165 | | - output.push_str("[unify.conflicts]\n"); |
166 | | - output.push_str(&format!( |
167 | | - "auto_resolve = {} # Pick highest version automatically\n", |
168 | | - config.unify.conflicts.auto_resolve |
169 | | - )); |
170 | | - output.push_str(&format!( |
171 | | - "resolution_mode = \"{}\" # \"permissive\" or \"strict\"\n", |
172 | | - config.unify.conflicts.resolution_mode |
173 | | - )); |
174 | | - output.push_str(&format!( |
175 | | - "add_markers = {} # Add # ⚠️ comments to Cargo.toml\n\n", |
176 | | - config.unify.conflicts.add_markers |
177 | | - )); |
178 | | - |
179 | | - // Transitive optimization |
180 | | - output.push_str("[unify.transitives]\n"); |
181 | | - output.push_str(&format!( |
182 | | - "consolidate_features = {} # Add fragmented transitive deps to workspace\n", |
183 | | - config.unify.transitives.consolidate_features |
184 | | - )); |
185 | | - |
186 | | - // Serialize host_selection |
187 | | - match &config.unify.transitives.host_selection { |
188 | | - crate::config::TransitiveFeatureHost::Auto => { |
189 | | - output.push_str("host_selection = \"auto\" # \"auto\", \"root\", \"largest\", or [\"crate-name\"]\n\n"); |
190 | | - } |
191 | | - crate::config::TransitiveFeatureHost::Root => { |
192 | | - output.push_str("host_selection = \"root\"\n\n"); |
193 | | - } |
194 | | - crate::config::TransitiveFeatureHost::Largest => { |
195 | | - output.push_str("host_selection = \"largest\"\n\n"); |
196 | | - } |
197 | | - crate::config::TransitiveFeatureHost::Explicit(names) => { |
198 | | - output.push_str("host_selection = ["); |
199 | | - for (i, name) in names.iter().enumerate() { |
200 | | - if i > 0 { |
201 | | - output.push_str(", "); |
202 | | - } |
203 | | - output.push_str(&format!("\"{}\"", name)); |
204 | | - } |
205 | | - output.push_str("]\n\n"); |
206 | | - } |
207 | | - } |
208 | | - |
209 | | - // Validation |
210 | | - output.push_str("[unify.validation]\n"); |
211 | | - if !config.unify.validation.targets.is_empty() { |
212 | | - output.push_str("targets = ["); |
213 | | - for (i, target) in config.unify.validation.targets.iter().enumerate() { |
214 | | - if i > 0 { |
215 | | - output.push_str(", "); |
216 | | - } |
217 | | - output.push_str(&format!("\"{}\"", target)); |
218 | | - } |
219 | | - output.push_str("]\n"); |
220 | | - } else { |
221 | | - output.push_str("targets = [] # Platform-specific validation (e.g., [\"x86_64-unknown-linux-gnu\"])\n"); |
222 | | - } |
223 | | - output.push_str(&format!( |
224 | | - "max_parallel_jobs = {} # 0 = auto-detect\n\n", |
225 | | - config.unify.validation.max_parallel_jobs |
226 | | - )); |
227 | | - |
228 | | - // Output |
229 | | - output.push_str("[unify.output]\n"); |
230 | | - output.push_str(&format!( |
231 | | - "generate_report = {}\n\n", |
232 | | - config.unify.output.generate_report |
233 | | - )); |
234 | | - |
235 | | - // Release |
236 | | - output.push_str("[release]\n"); |
237 | | - output.push_str(&format!("tag_prefix = \"{}\"\n", config.release.tag_prefix)); |
238 | | - output.push_str(&format!( |
239 | | - "tag_format = \"{}\" # Variables: {{crate}}, {{version}}\n", |
240 | | - config.release.tag_format |
241 | | - )); |
242 | | - output.push_str(&format!("require_clean = {}\n", config.release.require_clean)); |
243 | | - output.push_str(&format!("publish_delay = {}\n", config.release.publish_delay)); |
244 | | - output.push_str(&format!( |
245 | | - "create_github_release = {}\n", |
246 | | - config.release.create_github_release |
247 | | - )); |
248 | | - output.push_str(&format!("sign_tags = {}\n", config.release.sign_tags)); |
249 | | - output.push_str(&format!("changelog_path = \"{}\"\n", config.release.changelog_path)); |
250 | | - output.push_str(&format!( |
251 | | - "skip_changelog_for = {}\n", |
252 | | - if config.release.skip_changelog_for.is_empty() { |
253 | | - "[]".to_string() |
254 | | - } else { |
255 | | - format!( |
256 | | - "[{}]", |
257 | | - config |
258 | | - .release |
259 | | - .skip_changelog_for |
260 | | - .iter() |
261 | | - .map(|s| format!("\"{}\"", s)) |
262 | | - .collect::<Vec<_>>() |
263 | | - .join(", ") |
264 | | - ) |
265 | | - } |
266 | | - )); |
267 | | - output.push_str(&format!( |
268 | | - "require_changelog_entries = {}\n\n", |
269 | | - config.release.require_changelog_entries |
270 | | - )); |
271 | | - |
272 | | - // Splits - Don't include in default init |
273 | | - output.push_str("# Split/Sync: Use 'cargo rail split init <crate>' to configure individual crates\n"); |
274 | | - output.push_str("# [[splits]]\n"); |
275 | | - output.push_str("# name = \"my-crate\"\n"); |
276 | | - output .push_str("# remote = \"[email protected]:org/my-crate.git\"\n"); |
277 | | - output.push_str("# branch = \"main\"\n"); |
278 | | - output.push_str("# mode = \"single\"\n"); |
279 | | - output.push_str("# publish = true\n"); |
280 | | - output.push_str("#\n"); |
281 | | - output.push_str("# [[splits.paths]]\n"); |
282 | | - output.push_str("# crate = \"crates/my-crate\"\n"); |
283 | | - |
284 | | - Ok(output) |
285 | | -} |
286 | | - |
287 | 126 | /// Check if config file already exists at any location |
288 | 127 | fn check_existing_config(workspace_root: &Path) -> Option<PathBuf> { |
289 | 128 | crate::config::RailConfig::find_config_path(workspace_root) |
@@ -372,10 +211,17 @@ pub fn run_init_standalone( |
372 | 211 | }, |
373 | 212 | release: crate::config::ReleaseConfig::default(), |
374 | 213 | splits: vec![], |
| 214 | + formatting: crate::config::FormattingConfig::default(), |
375 | 215 | }; |
376 | 216 |
|
377 | | - // 5. Serialize with comments |
378 | | - let config_toml = serialize_config_with_comments(&config)?; |
| 217 | + // 5. Serialize with comments using Builder |
| 218 | + let config_toml = RailConfigBuilder::new() |
| 219 | + .header() |
| 220 | + .workspace(&config.workspace.root) |
| 221 | + .unify(&config.unify) |
| 222 | + .release(&config.release) |
| 223 | + .splits_template() |
| 224 | + .build()?; |
379 | 225 |
|
380 | 226 | // 4. Output |
381 | 227 | if dry_run { |
@@ -403,19 +249,28 @@ pub fn run_init_standalone( |
403 | 249 | #[cfg(test)] |
404 | 250 | mod tests { |
405 | 251 | use super::*; |
| 252 | + use crate::config::{FormattingConfig, ReleaseConfig}; |
406 | 253 |
|
407 | 254 | #[test] |
408 | | - fn test_serialize_config_with_comments() { |
| 255 | + fn test_serialize_config_with_builder() { |
409 | 256 | let config = RailConfig { |
410 | 257 | workspace: WorkspaceConfig { |
411 | 258 | root: PathBuf::from("."), |
412 | 259 | }, |
413 | 260 | unify: UnifyConfig::default(), |
414 | | - release: crate::config::ReleaseConfig::default(), |
| 261 | + release: ReleaseConfig::default(), |
415 | 262 | splits: vec![], |
| 263 | + formatting: FormattingConfig::default(), |
416 | 264 | }; |
417 | 265 |
|
418 | | - let toml = serialize_config_with_comments(&config).unwrap(); |
| 266 | + let toml = RailConfigBuilder::new() |
| 267 | + .header() |
| 268 | + .workspace(&config.workspace.root) |
| 269 | + .unify(&config.unify) |
| 270 | + .release(&config.release) |
| 271 | + .splits_template() |
| 272 | + .build() |
| 273 | + .unwrap(); |
419 | 274 |
|
420 | 275 | // Should contain section headers |
421 | 276 | assert!(toml.contains("[workspace]")); |
|
0 commit comments