refactor(ipc): consume generated bindings for library catalog/genre reads

FE cutover for the D1 slice: libraryGetCatalogYearBounds and
libraryGetGenreAlbumCounts now call the generated commands.* bindings instead of
a hand-written invoke<T>('cmd', ...), unwrapping the specta Result union (rethrow
on error — behavior-preserving). The duplicated hand DTOs collapse into named
aliases of the generated CatalogYearBoundsDto / GenreAlbumCountDto, so the shape
lives in one place (the contract) and consumers stay unchanged.

Verified the guard end to end: a serde-rename of the Rust field (contract change,
Rust still compiles) regenerated bindings and made tsc fail at the FE consumer;
reverted. cargo + tsc now enforce this slice's contract at compile time.
This commit is contained in:
Psychotoxical
2026-07-01 14:58:25 +02:00
parent b722b1abaf
commit fb5dee0701
2 changed files with 16 additions and 19 deletions
+5 -10
View File
@@ -4,6 +4,7 @@
* `lib/api/library.ts` god-module; the wrappers (reads/sync/stats/events) and the
* `@/lib/api/library` barrel re-export these, so consumers are unchanged.
*/
import type { CatalogYearBoundsDto, GenreAlbumCountDto } from '@/generated/bindings';
export interface TrackRefDto {
serverId: string;
@@ -363,16 +364,10 @@ export type PlaySessionYearBounds = {
maxYear: number | null;
};
export type CatalogYearBounds = {
minYear: number | null;
maxYear: number | null;
};
export type GenreAlbumCountRow = {
value: string;
albumCount: number;
songCount: number;
};
// Sourced from the tauri-specta contract (single source of truth); kept as named
// aliases so existing consumers stay unchanged while the shape lives in one place.
export type CatalogYearBounds = CatalogYearBoundsDto;
export type GenreAlbumCountRow = GenreAlbumCountDto;
export type LibraryGenreAlbumsRequest = {
serverId: string;
+11 -9
View File
@@ -4,6 +4,7 @@
* `@/lib/api/library` barrel.
*/
import { invoke } from '@tauri-apps/api/core';
import { commands } from '@/generated/bindings';
import { serverIndexKeyForId, mapServerIdFromIndexKey } from './internal';
import type {
CatalogYearBounds,
@@ -19,22 +20,23 @@ import type {
PlaySessionRecentTrack,
} from './dto';
export function libraryGetCatalogYearBounds(args: { serverId: string }): Promise<CatalogYearBounds> {
export async function libraryGetCatalogYearBounds(args: {
serverId: string;
}): Promise<CatalogYearBounds> {
const indexKey = serverIndexKeyForId(args.serverId);
return invoke<CatalogYearBounds>('library_get_catalog_year_bounds', {
serverId: indexKey,
});
const res = await commands.libraryGetCatalogYearBounds(indexKey);
if (res.status === 'error') throw new Error(res.error);
return res.data;
}
export function libraryGetGenreAlbumCounts(args: {
export async function libraryGetGenreAlbumCounts(args: {
serverId: string;
libraryScope?: string;
}): Promise<GenreAlbumCountRow[]> {
const indexKey = serverIndexKeyForId(args.serverId);
return invoke<GenreAlbumCountRow[]>('library_get_genre_album_counts', {
serverId: indexKey,
libraryScope: args.libraryScope,
});
const res = await commands.libraryGetGenreAlbumCounts(indexKey, args.libraryScope ?? null);
if (res.status === 'error') throw new Error(res.error);
return res.data;
}
/** Paginated albums for one genre from the local track index. */