Skip to content

Phase 0: format-aware export/import scaffold#10

Merged
xxvw merged 6 commits into
mainfrom
feat/export-format-scaffold
May 18, 2026
Merged

Phase 0: format-aware export/import scaffold#10
xxvw merged 6 commits into
mainfrom
feat/export-format-scaffold

Conversation

@xxvw
Copy link
Copy Markdown
Owner

@xxvw xxvw commented May 15, 2026

Summary

Phase 0 of the rekordbox / Serato / .cset interoperability work. Lands the
scaffold so subsequent phases (Phase 1+ for rekordbox XML, Phase 6+ for
Serato, etc.) only need to add plugins — the trait surface, the registry,
the Tauri commands, and the UI format picker are now in place.

  • New Format enum (cset / rekordbox-xml / rekordbox-usb /
    serato) with stable kebab-case ids pinned by a test, plus a
    TargetKind classifier (file / directory / in-place).
  • Exporter / Importer traits taking &mut Library (rusqlite needs
    the mutable borrow even for reads) plus ExportOptions /
    ImportOptions carrying destination + dry_run + an opaque
    serde_json::Value for format-specific knobs.
  • PluginRegistry keyed by Format with independent in/out
    registration. Empty by default — plugins register from Phase 1+.
  • Four new Tauri commands: list_export_formats, list_import_formats,
    library_export, library_import. When no plugin is registered for
    the chosen format, the command returns a clear "no exporter registered for {id}" error.
  • FormatPickerModal and two new toolbar buttons on the Library screen.
    Every format is rendered; unavailable ones come with a "coming soon"
    badge. Once picked, the appropriate save/open dialog opens based on
    TargetKind and dispatches the IPC call.

The legacy setlist_export / setlist_import path for a single
setlist's .cset is untouched. The existing export_preview /
export_execute (rekordbox-USB stub) is also untouched — that will be
unified into the new dispatch in Phase 11.

Test plan

  • cargo test --workspace — 5 new tests in conduction-export pass; no regressions.
  • npx tsc --noEmit in ui/ passes.
  • Visual: launch the app, open the Library screen, confirm both
    "Export Library…" and "Import Library…" buttons open the modal with
    all four rows greyed out. (Not part of CI; merge contingent on
    manual smoke test.)

xxvw added 6 commits May 15, 2026 14:00
… serato)

A new format module declares the four library-level export/import shapes
the user can pick from the UI, along with a TargetKind classifier
(file / directory / in-place) so callers can branch on how to ask for a
destination. FormatInfo is the IPC-facing descriptor (id, label,
target_kind, available) that the UI will use to render the format
selector.

Stable kebab-case ids ("cset", "rekordbox-xml", ...) are pinned by a
test so the IPC contract stays consistent. No plugins are registered
yet — that comes in subsequent commits.
A new api module defines:
- Exporter / Importer object-safe traits taking a &Library and per-direction
  Options struct, returning a per-direction Report.
- ExportOptions / ImportOptions carry destination + dry_run + an opaque
  serde_json::Value for format-specific knobs (USB volume label, conflict
  rules, etc.), so the IPC layer doesn't need a DTO per plugin.
- ConflictStrategy (skip / update / replace) controls how an importer
  handles existing rows.
- LibraryExportReport / LibraryImportReport keep the IPC schema on the
  Reports (utoipa::ToSchema) while leaving Options as internal-only types
  to avoid pulling PathBuf and serde_json::Value into the OpenAPI schema.

No plugins implement these traits yet — that lands in subsequent commits.
PluginRegistry holds Arc<dyn Exporter> / Arc<dyn Importer> keyed by Format
so the Tauri command layer can resolve "user picked rekordbox-xml" to a
concrete plugin. Each direction (in/out) is registered independently:
some plugins (rekordbox-usb) will only ship export at first, others
(serato) read and write the same shape.

export_formats() / import_formats() return FormatInfo with availability
flags so the UI can render every known format and grey out the ones not
yet implemented in this build.

default_registry() returns an empty registry for now; per-format
registration lands in Phase 1+ (rekordbox XML), Phase 6+ (serato), etc.
conduction-library's Library wraps a raw rusqlite::Connection without an
internal Mutex, so every method (including read-only ones) needs &mut
self. Aligning the Exporter / Importer trait signature with that lets
the registered importers actually call repo methods without working
around the borrow checker. Doc comment explains the rationale.

The stub plugins in registry tests are updated to match.
Adds an ExportRegistryHandle state holding an Arc<PluginRegistry>
populated at boot from conduction_export::default_registry(), plus four
new Tauri commands:

- list_export_formats / list_import_formats
    return Vec<FormatInfo> so the UI can render every known format with
    an availability flag.
- library_export / library_import
    take (format, path, ...) from the UI, build the matching
    ExportOptions / ImportOptions, and dispatch through the registry to
    the registered plugin. Until a plugin is registered for the chosen
    format, the command returns a clear "no exporter/importer
    registered for {id}" error so the UI can surface "coming soon".

The legacy setlist_export / setlist_import path (for .cset of a single
setlist) is untouched.
Adds two new toolbar buttons on the Library screen that open a modal
listing every Format the backend knows about (cset / rekordbox-xml /
rekordbox-usb / serato). Each row reads `available` from the registry
and is rendered as a disabled "coming soon" row when no plugin is
registered for that direction. Once a format is picked, a save/open
dialog appropriate for the format's TargetKind (file vs directory) is
opened and the result is dispatched through `library_export` /
`library_import`.

The new IPC types (FormatInfo, LibraryFormatId, ConflictStrategy,
LibraryExportReport, LibraryImportReport) mirror the Rust side.

CSS lives in a sibling file (FormatPickerModal.css) instead of App.css
to keep the modal's styling colocated with its component.
@xxvw xxvw merged commit 51d0a83 into main May 18, 2026
@xxvw xxvw deleted the feat/export-format-scaffold branch May 18, 2026 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant