Skip to content

Commit 3b89d74

Browse files
committed
Simplify peas mv command interface
- Accept suffix only (prefix stripped if provided) - Example: peas mv abc12 xyz99 (not peas mv peas-abc12 xyz99) - Random mode: warn but don't block on all-digits suffix - Sequential mode: still block on non-digits (--force to override)
1 parent ac96716 commit 3b89d74

File tree

3 files changed

+51
-44
lines changed

3 files changed

+51
-44
lines changed

src/cli/commands.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -311,16 +311,16 @@ pub enum Commands {
311311
fix: bool,
312312
},
313313

314-
/// Rename a ticket ID (change the suffix, keep the prefix)
314+
/// Rename a ticket ID
315315
///
316-
/// Example: `peas mv peas-abc12 xyz99` renames to peas-xyz99
316+
/// Example: `peas mv abc12 xyz99` renames peas-abc12 to peas-xyz99
317317
#[command(name = "mv")]
318318
Mv {
319-
/// The ticket ID to rename
320-
id: String,
319+
/// The old ID suffix (or full ID - prefix is stripped if present)
320+
old_id: String,
321321

322-
/// The new suffix (without prefix)
323-
new_suffix: String,
322+
/// The new ID suffix (or full ID - prefix is stripped if present)
323+
new_id: String,
324324

325325
/// Force rename even if suffix length or mode doesn't match config
326326
#[arg(long)]

src/cli/handlers/mv.rs

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,28 @@ use crate::config::{DATA_DIR, IdMode};
33
use anyhow::{Context, Result, bail};
44
use colored::Colorize;
55

6-
pub fn handle_mv(ctx: &CommandContext, id: String, new_suffix: String, force: bool) -> Result<()> {
6+
pub fn handle_mv(
7+
ctx: &CommandContext,
8+
old_suffix: String,
9+
new_suffix: String,
10+
force: bool,
11+
) -> Result<()> {
712
let prefix = &ctx.config.peas.prefix;
813
let id_length = ctx.config.peas.id_length;
914
let id_mode = ctx.config.peas.id_mode;
1015

16+
// Build full IDs from suffixes (strip prefix if user included it)
17+
let old_suffix = old_suffix.strip_prefix(prefix).unwrap_or(&old_suffix);
18+
let new_suffix = new_suffix.strip_prefix(prefix).unwrap_or(&new_suffix);
19+
20+
let old_id = format!("{}{}", prefix, old_suffix);
21+
let new_id = format!("{}{}", prefix, new_suffix);
22+
1123
// Validate source ticket exists
1224
let pea = ctx
1325
.repo
14-
.get(&id)
15-
.with_context(|| format!("Ticket not found: {}", id))?;
16-
17-
// Build the new ID
18-
let new_id = format!("{}{}", prefix, new_suffix);
26+
.get(&old_id)
27+
.with_context(|| format!("Ticket not found: {}", old_id))?;
1928

2029
// Check if new ID already exists
2130
if ctx.repo.get(&new_id).is_ok() {
@@ -34,9 +43,11 @@ pub fn handle_mv(ctx: &CommandContext, id: String, new_suffix: String, force: bo
3443
// Validate ID mode
3544
let is_all_digits = new_suffix.chars().all(|c| c.is_ascii_digit());
3645
match id_mode {
37-
IdMode::Random if is_all_digits && !force => {
38-
bail!(
39-
"Suffix '{}' is all digits but id_mode is 'random'. Use --force to override.",
46+
IdMode::Random if is_all_digits => {
47+
// Warn but don't block in random mode
48+
eprintln!(
49+
"{}: Suffix '{}' is all digits (unusual for random mode)",
50+
"warning".yellow().bold(),
4051
new_suffix
4152
);
4253
}
@@ -49,7 +60,7 @@ pub fn handle_mv(ctx: &CommandContext, id: String, new_suffix: String, force: bo
4960
_ => {}
5061
}
5162

52-
// Show warnings if force was used
63+
// Show warnings for force overrides
5364
if force {
5465
if new_suffix.len() != id_length {
5566
eprintln!(
@@ -59,26 +70,16 @@ pub fn handle_mv(ctx: &CommandContext, id: String, new_suffix: String, force: bo
5970
id_length
6071
);
6172
}
62-
match id_mode {
63-
IdMode::Random if is_all_digits => {
64-
eprintln!(
65-
"{}: Suffix '{}' is all digits but id_mode is 'random'",
66-
"warning".yellow().bold(),
67-
new_suffix
68-
);
69-
}
70-
IdMode::Sequential if !is_all_digits => {
71-
eprintln!(
72-
"{}: Suffix '{}' contains non-digits but id_mode is 'sequential'",
73-
"warning".yellow().bold(),
74-
new_suffix
75-
);
76-
}
77-
_ => {}
73+
if id_mode == IdMode::Sequential && !is_all_digits {
74+
eprintln!(
75+
"{}: Suffix '{}' contains non-digits but id_mode is 'sequential'",
76+
"warning".yellow().bold(),
77+
new_suffix
78+
);
7879
}
7980
}
8081

81-
println!("Renaming {} → {}", id, new_id);
82+
println!("Renaming {} → {}", old_id, new_id);
8283

8384
// Find all tickets that reference this ID
8485
let all_peas = ctx.repo.list()?;
@@ -89,26 +90,32 @@ pub fn handle_mv(ctx: &CommandContext, id: String, new_suffix: String, force: bo
8990

9091
// Update references in other tickets
9192
for other_pea in &all_peas {
92-
if other_pea.id == id {
93+
if other_pea.id == old_id {
9394
continue; // Skip the ticket we're renaming
9495
}
9596

9697
let mut needs_update = false;
9798
let mut updated_pea = other_pea.clone();
9899

99100
// Check parent reference
100-
if updated_pea.parent.as_ref() == Some(&id) {
101+
if updated_pea.parent.as_ref() == Some(&old_id) {
101102
updated_pea.parent = Some(new_id.clone());
102103
needs_update = true;
103104
updated_parents += 1;
104105
}
105106

106107
// Check blocking references
107-
if updated_pea.blocking.contains(&id) {
108+
if updated_pea.blocking.contains(&old_id) {
108109
updated_pea.blocking = updated_pea
109110
.blocking
110111
.iter()
111-
.map(|b| if b == &id { new_id.clone() } else { b.clone() })
112+
.map(|b| {
113+
if b == &old_id {
114+
new_id.clone()
115+
} else {
116+
b.clone()
117+
}
118+
})
112119
.collect();
113120
needs_update = true;
114121
updated_blocking += 1;
@@ -126,7 +133,7 @@ pub fn handle_mv(ctx: &CommandContext, id: String, new_suffix: String, force: bo
126133
// Get old and new file paths
127134
let old_filename = format!(
128135
"{}--{}.md",
129-
id,
136+
old_id,
130137
slug::slugify(&pea.title)
131138
.chars()
132139
.take(50)
@@ -160,16 +167,16 @@ pub fn handle_mv(ctx: &CommandContext, id: String, new_suffix: String, force: bo
160167
let undo_path = data_dir.join(".undo");
161168
if undo_path.exists() {
162169
let undo_content = std::fs::read_to_string(&undo_path)?;
163-
if undo_content.contains(&id) {
164-
let updated_undo = undo_content.replace(&id, &new_id);
170+
if undo_content.contains(&old_id) {
171+
let updated_undo = undo_content.replace(&old_id, &new_id);
165172
// Also update file paths in undo
166173
let updated_undo = updated_undo.replace(&old_filename, &new_filename);
167174
std::fs::write(&undo_path, updated_undo)?;
168175
println!(" Updated .undo file");
169176
}
170177
}
171178

172-
println!("{} Renamed {} → {}", "✓".green(), id, new_id);
179+
println!("{} Renamed {} → {}", "✓".green(), old_id, new_id);
173180
if updated_parents > 0 {
174181
println!(" Updated {} parent reference(s)", updated_parents);
175182
}

src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@ fn main() -> Result<()> {
136136
Commands::Asset { action } => peas::cli::handlers::handle_asset(&ctx, action),
137137
Commands::Undo { json } => peas::cli::handlers::handle_undo(&ctx, json),
138138
Commands::Mv {
139-
id,
140-
new_suffix,
139+
old_id,
140+
new_id,
141141
force,
142-
} => peas::cli::handlers::handle_mv(&ctx, id, new_suffix, force),
142+
} => peas::cli::handlers::handle_mv(&ctx, old_id, new_id, force),
143143
}
144144
}
145145
}

0 commit comments

Comments
 (0)