import { invoke } from '@tauri-apps/api/core' import { listen, type UnlistenFn } from '@tauri-apps/api/event' export type InstanceBackupEligibility = | 'eligible' | 'not_installed' | 'symlink_instance' | 'direct_linked_instance' | 'external_game_directory' | 'root_missing' | 'root_is_link' export interface BackupRepositoryStatus { path: string initialized: boolean available: boolean stored_size: number logical_size: number snapshot_count: number error: string | null } export type BackupExclusionKind = 'file' | 'directory' export interface BackupExclusion { path: string kind: BackupExclusionKind } export function formatBackupExclusionPath(exclusion: BackupExclusion): string { return exclusion.kind === 'directory' ? `${exclusion.path}/` : exclusion.path } export interface InstanceBackupConfig { instance_id: string enabled: boolean eligibility: InstanceBackupEligibility excluded_paths: BackupExclusion[] snapshot_count: number } export interface BackupSnapshot { id: string instance_id: string instance_name: string created_at: number file_count: number symlink_count: number logical_size: number added_size: number } export interface BackupDeleteSummary { snapshot_count: number logical_size: number } export interface BackupRestorePreview { added_files: number modified_files: number deleted_files: number plan_token: string } export type BackupOperationType = 'create' | 'restore' | 'restore_preview' | 'delete' | 'repository_move' export type BackupOperationState = | 'queued' | 'scanning' | 'hashing' | 'saving' | 'validating' | 'materializing' | 'applying' | 'completed' | 'cancelled' | 'failed' | 'interrupted' export interface BackupOperation { id: string operation_type: BackupOperationType instance_id: string snapshot_id?: string state: BackupOperationState processed_bytes: number total_bytes: number cancellable: boolean cancel_requested: boolean error?: string created_at: number updated_at: number finished_at?: number restore_preview?: BackupRestorePreview } export type BackupProgressStage = | 'scanning' | 'hashing' | 'saving' | 'validating' | 'copying' | 'restoring' | 'deleting' | 'completed' | 'cancelled' | 'failed' export type BackupOperationFinalState = 'completed' | 'cancelled' | 'failed' export interface BackupProgressEvent { operationId: string instanceId?: string operationType: BackupOperationType stage: BackupProgressStage processedBytes: number totalBytes: number finalState?: BackupOperationFinalState snapshotId?: string message?: string } export function canonicalizeBackupExclusions(values: BackupExclusion[]): BackupExclusion[] { const normalized = values .map((value) => ({ ...value, path: value.path.replaceAll('\\', '/') })) .sort( (left, right) => left.path.split('/').length - right.path.split('/').length || left.path.localeCompare(right.path), ) const result: BackupExclusion[] = [] for (const value of normalized) { const duplicate = result.find((existing) => existing.path === value.path) if (duplicate) { if (value.kind === 'directory') duplicate.kind = 'directory' continue } if ( result.some( (parent) => parent.kind === 'directory' && value.path.startsWith(`${parent.path}/`), ) ) { continue } result.push(value) } return result } export function getBackupRepositoryStatus(): Promise { return invoke('plugin:instance|instance_get_backup_repository_status') } export function moveBackupRepository(destination: string): Promise { return invoke('plugin:instance|instance_move_backup_repository', { destination }) } export function getBackupConfig(instanceId: string): Promise { return invoke('plugin:instance|instance_get_backup_config', { instanceId }) } export function normalizeBackupExclusion( instanceId: string, selectedPath: string, kind: BackupExclusionKind, ): Promise { return invoke('plugin:instance|instance_normalize_backup_exclusion', { instanceId, selectedPath, kind, }) } export function enableBackups( instanceId: string, excludedPaths: BackupExclusion[], ): Promise { return invoke('plugin:instance|instance_enable_backups', { instanceId, excludedPaths }) } export function updateBackupExclusions( instanceId: string, excludedPaths: BackupExclusion[], ): Promise { return invoke('plugin:instance|instance_update_backup_exclusions', { instanceId, excludedPaths, }) } export function disableBackups(instanceId: string): Promise { return invoke('plugin:instance|instance_disable_backups', { instanceId }) } export function startBackup(instanceId: string): Promise { return invoke('plugin:instance|instance_start_backup', { instanceId }) } export function cancelBackup(operationId: string): Promise { return invoke('plugin:instance|instance_cancel_backup', { operationId }) } export function listBackupOperations( instanceId?: string, activeOnly = false, ): Promise { return invoke('plugin:instance|instance_list_backup_operations', { instanceId, activeOnly, }) } export function listBackups(instanceId: string): Promise { return invoke('plugin:instance|instance_list_backups', { instanceId }) } export function deleteBackup(snapshotId: string): Promise { return invoke('plugin:instance|instance_delete_backup', { snapshotId }) } export function startBackupRestorePreview(snapshotId: string): Promise { return invoke('plugin:instance|instance_get_backup_restore_preview', { snapshotId }) } export function restoreBackup(snapshotId: string, planToken: string): Promise { return invoke('plugin:instance|instance_restore_backup', { snapshotId, planToken }) } export function getBackupDeleteSummary(instanceId: string): Promise { return invoke('plugin:instance|instance_get_backup_delete_summary', { instanceId }) } export function listenBackupProgress( handler: (event: BackupProgressEvent) => void, ): Promise { return listen('instance_backup_progress', (event) => handler(event.payload)) }