Skip to content

Commit 25d181b

Browse files
committed
cargo-rail: fixing the command structure; final cleanup?
1 parent f3e7b69 commit 25d181b

File tree

8 files changed

+276
-59
lines changed

8 files changed

+276
-59
lines changed

src/commands/init.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,21 @@ pub fn run_init(
2222
let workspace_root = ctx.workspace_root();
2323
let config_path = workspace_root.join(output_path);
2424

25-
if let Some(existing) = check_existing_config(workspace_root) {
25+
// Check if target path already exists
26+
if config_path.exists() {
2627
if !force {
2728
return Err(RailError::with_help(
28-
format!("configuration exists: {}", existing.display()),
29+
format!("configuration exists: {}", config_path.display()),
2930
"use --force to overwrite",
3031
));
3132
}
3233
if !check {
33-
eprintln!("overwriting: {}", existing.display());
34+
eprintln!("overwriting: {}", config_path.display());
35+
}
36+
} else if let Some(existing) = check_existing_config(workspace_root) {
37+
// A config exists elsewhere - warn but allow writing to different path
38+
if !check {
39+
eprintln!("note: existing config at {}", existing.display());
3440
}
3541
}
3642

@@ -143,15 +149,21 @@ pub fn run_init_standalone(
143149
) -> RailResult<()> {
144150
let config_path = workspace_root.join(output_path);
145151

146-
if let Some(existing) = check_existing_config(workspace_root) {
152+
// Check if target path already exists
153+
if config_path.exists() {
147154
if !force {
148155
return Err(RailError::with_help(
149-
format!("configuration exists: {}", existing.display()),
156+
format!("configuration exists: {}", config_path.display()),
150157
"use --force to overwrite",
151158
));
152159
}
153160
if !check {
154-
eprintln!("overwriting: {}", existing.display());
161+
eprintln!("overwriting: {}", config_path.display());
162+
}
163+
} else if let Some(existing) = check_existing_config(workspace_root) {
164+
// A config exists elsewhere - warn but allow writing to different path
165+
if !check {
166+
eprintln!("note: existing config at {}", existing.display());
155167
}
156168
}
157169

src/commands/release.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,11 @@ pub fn run_release_check(
197197
}
198198

199199
/// Initialize release configuration
200-
pub fn run_release_init(ctx: &WorkspaceContext, crates: Option<&str>, check: bool) -> RailResult<()> {
200+
pub fn run_release_init(ctx: &WorkspaceContext, crates: Option<Vec<String>>, check: bool) -> RailResult<()> {
201201
use crate::config::{ChangelogConfig, CrateReleaseConfig, RailConfig};
202202
use std::fs;
203203

204-
let requested_crates: Option<Vec<String>> = crates.map(|s| {
205-
s.split(',')
206-
.map(|name| name.trim().to_string())
207-
.filter(|name| !name.is_empty())
208-
.collect()
209-
});
204+
let requested_crates = crates;
210205

211206
let members = ctx.cargo.metadata().workspace_packages();
212207
let workspace_root = ctx.workspace_root();

src/commands/split.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,11 @@ pub fn run_split(
107107
}
108108

109109
/// Initialize split configuration for crates
110-
pub fn run_split_init(ctx: &WorkspaceContext, crates: Option<&str>, check: bool) -> RailResult<()> {
110+
pub fn run_split_init(ctx: &WorkspaceContext, crates: Option<Vec<String>>, check: bool) -> RailResult<()> {
111111
use crate::config::RailConfig;
112112
use std::fs;
113113

114-
let requested_crates: Option<Vec<String>> = crates.map(|s| {
115-
s.split(',')
116-
.map(|name| name.trim().to_string())
117-
.filter(|name| !name.is_empty())
118-
.collect()
119-
});
114+
let requested_crates = crates;
120115

121116
let splits = detect_workspace_splits(ctx, requested_crates.as_deref())?;
122117

src/main.rs

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ enum Commands {
3030
#[arg(long)]
3131
since: Option<String>,
3232
/// Start ref (for SHA pair mode)
33-
#[arg(long, conflicts_with = "since")]
33+
#[arg(long, conflicts_with = "since", requires = "to")]
3434
from: Option<String>,
3535
/// End ref (for SHA pair mode)
3636
#[arg(long, requires = "from")]
@@ -125,8 +125,10 @@ enum Commands {
125125

126126
/// Split a crate to a standalone repository with git history
127127
Split {
128-
/// Crate name, or 'init' to configure
129-
crate_name: Option<String>,
128+
/// Action: 'init' to configure splits, or crate name to split
129+
action: Option<String>,
130+
/// Additional crate name(s) for init
131+
crate_names: Vec<String>,
130132
/// Split all configured crates
131133
#[arg(short, long)]
132134
all: bool,
@@ -357,32 +359,26 @@ fn main() {
357359

358360
// Split/Sync
359361
Commands::Split {
360-
crate_name,
362+
action,
363+
crate_names,
361364
all,
362365
remote,
363366
check,
364367
format,
365368
} => {
366-
// Check if this is 'split init <crates>'
367-
if let Some(name) = crate_name {
368-
if name == "init" {
369-
// cargo rail split init (all crates)
370-
commands::run_split_init(&ctx, None, check)
371-
} else if name.starts_with("init,") || name.starts_with("init ") {
372-
// cargo rail split "init,crate1,crate2" (specific crates)
373-
let crates = name
374-
.strip_prefix("init,")
375-
.or_else(|| name.strip_prefix("init "))
376-
.unwrap()
377-
.trim();
378-
commands::run_split_init(&ctx, Some(crates), check)
369+
// Handle init subcommand: cargo rail split init [crate1 crate2 ...]
370+
if action.as_deref() == Some("init") {
371+
let crates = if crate_names.is_empty() {
372+
None
379373
} else {
380-
// cargo rail split mycrate (regular split)
381-
commands::run_split(&ctx, Some(name.clone()), all, remote.clone(), check, format)
382-
}
374+
Some(crate_names)
375+
};
376+
commands::run_split_init(&ctx, crates, check)
383377
} else {
384-
// cargo rail split --all or cargo rail split --check
385-
commands::run_split(&ctx, None, all, remote.clone(), check, format)
378+
// Regular split command
379+
// If action is provided and not "init", treat it as the crate name
380+
let crate_name = action;
381+
commands::run_split(&ctx, crate_name, all, remote.clone(), check, format)
386382
}
387383
}
388384
Commands::Sync {
@@ -422,12 +418,12 @@ fn main() {
422418
// Handle init subcommand
423419
if action.as_deref() == Some("init") {
424420
// cargo rail release init <crates>
425-
let crates_str = if crate_names.is_empty() {
421+
let crates = if crate_names.is_empty() {
426422
None
427423
} else {
428-
Some(crate_names.join(","))
424+
Some(crate_names)
429425
};
430-
commands::run_release_init(&ctx, crates_str.as_deref(), check)
426+
commands::run_release_init(&ctx, crates, check)
431427
} else {
432428
// Regular release command
433429
// If action is provided and not "init", treat it as the first crate name

src/release/planner.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::config::{ChangelogRelativeTo, ReleaseConfig};
44
use crate::error::{RailError, RailResult};
5-
use crate::release::version::{BumpType, VersionBumper};
5+
use crate::release::version::BumpType;
66
use crate::workspace::WorkspaceContext;
77
use semver::Version;
88
use serde::Serialize;
@@ -127,8 +127,8 @@ impl<'a> ReleasePlanner<'a> {
127127

128128
let manifest_path = package.manifest_path.clone().into_std_path_buf();
129129

130-
// Get current version
131-
let current_version = VersionBumper::get_version(&manifest_path)?;
130+
// Get current version from cargo_metadata (already resolves workspace inheritance)
131+
let current_version = package.version.clone();
132132

133133
// Calculate new version
134134
let new_version = bump_type.apply(&current_version);

src/release/version.rs

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,23 +121,52 @@ impl VersionBumper {
121121
/// Bump version in Cargo.toml
122122
///
123123
/// Uses lossless TOML editing to preserve comments and formatting.
124+
///
125+
/// # Errors
126+
/// Returns an error if the crate uses workspace version inheritance.
124127
pub fn bump_version(manifest_path: &Path, bump_type: BumpType) -> RailResult<Version> {
125128
use crate::toml::editor::TomlEditor;
126129

127130
let mut editor = TomlEditor::open(manifest_path)?;
128131
let doc = editor.doc();
129132

130-
// Get current version
131-
let current_str = doc
132-
.get("package")
133-
.and_then(|p| p.get("version"))
134-
.and_then(|v| v.as_str())
135-
.ok_or_else(|| {
136-
RailError::with_help(
137-
format!("No version field in {}", manifest_path.display()),
138-
"Ensure [package.version] is set",
139-
)
140-
})?;
133+
// Get current version - check for workspace inheritance
134+
let version_item = doc.get("package").and_then(|p| p.get("version"));
135+
136+
// Check if using workspace inheritance
137+
if let Some(item) = version_item
138+
&& (item.is_inline_table() || item.is_table())
139+
{
140+
// Check for { workspace = true }
141+
if let Some(tbl) = item.as_inline_table() {
142+
if tbl.get("workspace").and_then(|v| v.as_bool()) == Some(true) {
143+
return Err(RailError::with_help(
144+
format!(
145+
"Cannot bump version in {}: uses workspace inheritance",
146+
manifest_path.display()
147+
),
148+
"Set a specific version in this crate's Cargo.toml, or update [workspace.package.version] in the root Cargo.toml",
149+
));
150+
}
151+
} else if let Some(tbl) = item.as_table()
152+
&& tbl.get("workspace").and_then(|v| v.as_bool()) == Some(true)
153+
{
154+
return Err(RailError::with_help(
155+
format!(
156+
"Cannot bump version in {}: uses workspace inheritance",
157+
manifest_path.display()
158+
),
159+
"Set a specific version in this crate's Cargo.toml, or update [workspace.package.version] in the root Cargo.toml",
160+
));
161+
}
162+
}
163+
164+
let current_str = version_item.and_then(|v| v.as_str()).ok_or_else(|| {
165+
RailError::with_help(
166+
format!("No version field in {}", manifest_path.display()),
167+
"Ensure [package.version] is set",
168+
)
169+
})?;
141170

142171
let current = Version::parse(current_str).map_err(|e| {
143172
RailError::message(format!(

0 commit comments

Comments
 (0)