Route every removal path through the batch interface #21

Merged
wokoman merged 1 commit from refactor/one-removal-path into main 2026-08-28 20:28:41 +02:00
Owner

Every way of removing tracks — ironpod rm, ironpod dedupe, and pressing d in the browser — now goes through the batch interface, and the single-track primitive is private so the guarantee cannot be bypassed. This is what closes #2 properly: the earlier fix made the two command-line paths behave, this removes the reason they could diverge in the first place. One implementation, no call site re-implementing "loop, then write once".

One change to the interface pinned in #4, which is worth your eye. before_each(done, total) could not express the database write, and #5 asks for no visible regression in the browser — which draws a Writing the iPod database… frame there today. Leaving it out would mean the browser blocks on the write with a stale "Deleting 5 of 5" on screen, and SPEC.md records that a blocking step drawing nothing is a bug, not a detail. The callback is now on_step(RemoveStep), either Deleting { done, total } or Writing — the same shape #8 introduced for copy plans, so the two batch operations report progress the same way. Both constraints came from the same design session and they conflict; I resolved it towards the user-visible behaviour.

Two consequences worth naming. RemoveFailed's Display already reads "removed 1 of 3: ", so neither front end prepends its own count any more — the browser's status line is that string, and the command line lets the error carry it rather than printing a count and then an error that repeats it. And the regression test I added for #2 is deleted, not lost: its subject moved into remove_all, where a_failure_partway_stops_there_and_keeps_the_rest already asserts the same thing against the same forced failure. Keeping both would have been one test in two places.

The integration tests move over too — removing one track is now expressed as a batch of one, with no surviving special case.

Verified on the device (iTunesDB, iTunesDB.ext and SysInfo backed up first, and only throwaway tracks touched — nothing from the existing 6307-track library):

  • Four marker tracks copied on, then two removed with ironpod rm, one removed in the browser, and the last removed with rm again.
  • The browser was driven through expect at a real terminal size: confirmation popup, Deleting 1 of 1 track…, then Writing the iPod database… — the write frame is intact, which is what the interface change was for.
  • ironpod dedupe --dry-run runs clean against the real library.
  • Track count back to 6307 with nothing matching the marker artist left behind.

Not verified: that the device plays what remains. That needs unplugging and using it, which is yours to do — the database and the files agree, and nothing is listed that has no file.

67 unit tests pass, cargo clippy --all-targets clean.

Closes #5. Closes #2.

Every way of removing tracks — `ironpod rm`, `ironpod dedupe`, and pressing `d` in the browser — now goes through the batch interface, and the single-track primitive is private so the guarantee cannot be bypassed. This is what closes #2 properly: the earlier fix made the two command-line paths behave, this removes the reason they could diverge in the first place. One implementation, no call site re-implementing "loop, then write once". **One change to the interface pinned in #4, which is worth your eye.** `before_each(done, total)` could not express the database write, and #5 asks for no visible regression in the browser — which draws a `Writing the iPod database…` frame there today. Leaving it out would mean the browser blocks on the write with a stale "Deleting 5 of 5" on screen, and `SPEC.md` records that a blocking step drawing nothing is a bug, not a detail. The callback is now `on_step(RemoveStep)`, either `Deleting { done, total }` or `Writing` — the same shape #8 introduced for copy plans, so the two batch operations report progress the same way. Both constraints came from the same design session and they conflict; I resolved it towards the user-visible behaviour. Two consequences worth naming. `RemoveFailed`'s `Display` already reads "removed 1 of 3: <reason>", so neither front end prepends its own count any more — the browser's status line is that string, and the command line lets the error carry it rather than printing a count and then an error that repeats it. And the regression test I added for #2 is deleted, not lost: its subject moved into `remove_all`, where `a_failure_partway_stops_there_and_keeps_the_rest` already asserts the same thing against the same forced failure. Keeping both would have been one test in two places. The integration tests move over too — removing one track is now expressed as a batch of one, with no surviving special case. **Verified on the device** (`iTunesDB`, `iTunesDB.ext` and `SysInfo` backed up first, and only throwaway tracks touched — nothing from the existing 6307-track library): - Four marker tracks copied on, then two removed with `ironpod rm`, one removed in the browser, and the last removed with `rm` again. - The browser was driven through `expect` at a real terminal size: confirmation popup, `Deleting 1 of 1 track…`, then `Writing the iPod database…` — the write frame is intact, which is what the interface change was for. - `ironpod dedupe --dry-run` runs clean against the real library. - Track count back to 6307 with nothing matching the marker artist left behind. Not verified: that the device *plays* what remains. That needs unplugging and using it, which is yours to do — the database and the files agree, and nothing is listed that has no file. 67 unit tests pass, `cargo clippy --all-targets` clean. Closes #5. Closes #2.
The two command-line paths and the browser each re-implemented "loop,
then write once", and only the browser got the failure behaviour right.
Single-track removal is private now, so the guarantee cannot be bypassed;
removing one track is a batch of one.

Closes #5. Closes #2.
wokoman deleted branch refactor/one-removal-path 2026-08-28 20:28:41 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
wokoman/ironpod!21
No description provided.