Add deck rename and duplicate operations
New Catalog::rename_deck / duplicate_deck (duplicating format, commander, notes, and every deck_cards row under a new name), with tests covering the UNIQUE(name) collision cases the UI needs to surface as friendly errors rather than raw SQL messages. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,36 @@ impl Catalog {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rename a deck. Fails if another deck already has that name (`name` is
|
||||
/// UNIQUE).
|
||||
pub fn rename_deck(&self, id: i64, name: &str) -> Result<()> {
|
||||
self.conn
|
||||
.execute("UPDATE decks SET name = ?1 WHERE id = ?2", params![name, id])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Duplicate a deck under a new name: same format/commander/notes, plus a
|
||||
/// copy of every deck_cards row (quantity + commander flag). Fails if
|
||||
/// `new_name` is already taken.
|
||||
pub fn duplicate_deck(&self, id: i64, new_name: &str) -> Result<Deck> {
|
||||
let src = self
|
||||
.get_deck(id)?
|
||||
.ok_or_else(|| crate::error::CoreError::Other(format!("deck {id} not found")))?;
|
||||
self.conn.execute(
|
||||
"INSERT INTO decks(name, format, commander_oracle_id, notes)
|
||||
VALUES (?1, ?2, ?3, ?4)",
|
||||
params![new_name, src.format, src.commander_oracle_id, src.notes],
|
||||
)?;
|
||||
let new_id = self.conn.last_insert_rowid();
|
||||
self.conn.execute(
|
||||
"INSERT INTO deck_cards(deck_id, oracle_id, quantity, is_commander)
|
||||
SELECT ?1, oracle_id, quantity, is_commander FROM deck_cards WHERE deck_id = ?2",
|
||||
params![new_id, id],
|
||||
)?;
|
||||
self.get_deck(new_id)
|
||||
.map(|d| d.expect("just-inserted deck must exist"))
|
||||
}
|
||||
|
||||
/// Set how many copies of a card belong to a deck. `quantity <= 0` removes
|
||||
/// the card from the deck. Upserts on (deck_id, oracle_id).
|
||||
pub fn set_deck_card(
|
||||
@@ -286,4 +316,57 @@ mod tests {
|
||||
cat.delete_deck(d.id).unwrap();
|
||||
assert!(cat.card_decks("lightning-bolt").unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_deck_updates_name_and_rejects_duplicates() {
|
||||
let fx = test_support::make();
|
||||
let cat = &fx.catalog;
|
||||
let a = cat.create_deck("Deck A", None).unwrap();
|
||||
cat.create_deck("Deck B", None).unwrap();
|
||||
|
||||
cat.rename_deck(a.id, "Deck A Renamed").unwrap();
|
||||
assert_eq!(cat.get_deck(a.id).unwrap().unwrap().name, "Deck A Renamed");
|
||||
|
||||
// decks.name is UNIQUE.
|
||||
assert!(cat.rename_deck(a.id, "Deck B").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_deck_copies_metadata_and_cards() {
|
||||
let fx = test_support::make();
|
||||
let cat = &fx.catalog;
|
||||
cat.add_owned("llanowar-elves", 2).unwrap();
|
||||
|
||||
let src = cat.create_deck("Original", Some("llanowar-elves")).unwrap();
|
||||
cat.set_deck_format(src.id, "Commander").unwrap();
|
||||
cat.set_deck_card(src.id, "llanowar-elves", 2, true).unwrap();
|
||||
cat.set_deck_card(src.id, "lightning-bolt", 1, false).unwrap();
|
||||
|
||||
let copy = cat.duplicate_deck(src.id, "Original (copy)").unwrap();
|
||||
assert_eq!(copy.name, "Original (copy)");
|
||||
assert_eq!(copy.format, "Commander");
|
||||
assert_eq!(copy.commander_oracle_id.as_deref(), Some("llanowar-elves"));
|
||||
|
||||
let mut contents: Vec<_> = cat
|
||||
.deck_contents(copy.id)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|e| (e.card.oracle_id, e.quantity, e.is_commander))
|
||||
.collect();
|
||||
contents.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
assert_eq!(
|
||||
contents,
|
||||
vec![
|
||||
("lightning-bolt".to_string(), 1, false),
|
||||
("llanowar-elves".to_string(), 2, true),
|
||||
]
|
||||
);
|
||||
|
||||
// The original deck is untouched.
|
||||
let original_contents = cat.deck_contents(src.id).unwrap();
|
||||
assert_eq!(original_contents.len(), 2);
|
||||
|
||||
// Duplicating into an already-taken name fails (UNIQUE).
|
||||
assert!(cat.duplicate_deck(src.id, "Original").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user