cleanup of new result code
This commit is contained in:
parent
498e2611ae
commit
5a032a87ae
23 changed files with 323 additions and 303 deletions
|
|
@ -1,22 +1,22 @@
|
|||
import { Notice } from 'obsidian';
|
||||
import type { AppError } from 'packages/obsidian/src/utils/AppError';
|
||||
import { AppErrorKind } from 'packages/obsidian/src/utils/AppError';
|
||||
import { Logger } from 'packages/obsidian/src/utils/Logger';
|
||||
import type { MDBError } from 'packages/obsidian/src/utils/MDBError';
|
||||
import { MDBErrorKind } from 'packages/obsidian/src/utils/MDBError';
|
||||
|
||||
export class ErrorReporter {
|
||||
notice(error: AppError): void {
|
||||
notice(error: MDBError): void {
|
||||
const message = error.userMessage ?? error.message;
|
||||
new Notice(message);
|
||||
}
|
||||
|
||||
log(error: AppError): void {
|
||||
log(error: MDBError): void {
|
||||
Logger.warn('MDB | error', error);
|
||||
}
|
||||
|
||||
report(error: AppError): void {
|
||||
report(error: MDBError): void {
|
||||
this.log(error);
|
||||
|
||||
if (error.kind !== AppErrorKind.Cancelled) {
|
||||
if (error.kind !== MDBErrorKind.Cancelled) {
|
||||
this.notice(error);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export enum AppErrorKind {
|
||||
export enum MDBErrorKind {
|
||||
Validation = 'Validation',
|
||||
Api = 'Api',
|
||||
Network = 'Network',
|
||||
|
|
@ -8,17 +8,15 @@ export enum AppErrorKind {
|
|||
Unexpected = 'Unexpected',
|
||||
}
|
||||
|
||||
export interface AppError {
|
||||
kind: AppErrorKind;
|
||||
export interface MDBError {
|
||||
kind: MDBErrorKind;
|
||||
message: string;
|
||||
userMessage?: string;
|
||||
cause?: unknown;
|
||||
context?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export const appError = (error: AppError): AppError => error;
|
||||
|
||||
export const toAppError = (cause: unknown, fallback: Omit<AppError, 'cause'>): AppError => {
|
||||
export function toMdbError(cause: unknown, fallback: Omit<MDBError, 'cause'>): MDBError {
|
||||
const message = cause instanceof Error ? cause.message : String(cause);
|
||||
return { ...fallback, message: fallback.message || message, cause };
|
||||
};
|
||||
}
|
||||
|
|
@ -5,8 +5,8 @@ import type { SeasonSelectModalElement } from 'packages/obsidian/src/modals/Medi
|
|||
import { MediaDbSeasonSelectModal } from 'packages/obsidian/src/modals/MediaDbSeasonSelectModal';
|
||||
import type { MediaTypeModel } from 'packages/obsidian/src/models/MediaTypeModel';
|
||||
import type { SeasonModel } from 'packages/obsidian/src/models/SeasonModel';
|
||||
import type { AppError } from 'packages/obsidian/src/utils/AppError';
|
||||
import { AppErrorKind } from 'packages/obsidian/src/utils/AppError';
|
||||
import type { MDBError } from 'packages/obsidian/src/utils/MDBError';
|
||||
import { MDBErrorKind } from 'packages/obsidian/src/utils/MDBError';
|
||||
import { MediaType } from 'packages/obsidian/src/utils/MediaType';
|
||||
import type { SearchModalOptions } from 'packages/obsidian/src/utils/ModalHelper';
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ export class MediaDbEntryHelper {
|
|||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private reportAppError(error: AppError): void {
|
||||
private reportMdbError(error: MDBError): void {
|
||||
this.plugin.errorReporter.report(error);
|
||||
}
|
||||
|
||||
|
|
@ -29,12 +29,12 @@ export class MediaDbEntryHelper {
|
|||
|
||||
const apiSearchResults = await this.plugin.apiManager.query(advancedSearch.query, advancedSearch.apis);
|
||||
if (!apiSearchResults.ok) {
|
||||
this.reportAppError(apiSearchResults.error);
|
||||
this.reportMdbError(apiSearchResults.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!apiSearchResults.value.items || apiSearchResults.value.items.length === 0) {
|
||||
this.reportAppError({ kind: AppErrorKind.Validation, message: 'No results found.', userMessage: 'No results found.' });
|
||||
this.reportMdbError({ kind: MDBErrorKind.Validation, message: 'No results found.', userMessage: 'No results found.' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -66,18 +66,18 @@ export class MediaDbEntryHelper {
|
|||
const apis = this.plugin.apiManager.apis.filter(api => api.hasTypeOverlap(types)).map(api => api.apiName);
|
||||
const apiSearchResults = await this.plugin.apiManager.query(searchData.query, apis);
|
||||
if (!apiSearchResults.ok) {
|
||||
this.reportAppError(apiSearchResults.error);
|
||||
this.reportMdbError(apiSearchResults.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!apiSearchResults.value.items || apiSearchResults.value.items.length === 0) {
|
||||
this.reportAppError({ kind: AppErrorKind.Validation, message: 'No results found.', userMessage: 'No results found.' });
|
||||
this.reportMdbError({ kind: MDBErrorKind.Validation, message: 'No results found.', userMessage: 'No results found.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const filteredSearchResults = apiSearchResults.value.items.filter(result => types.includes(result.getMediaType()));
|
||||
if (filteredSearchResults.length === 0) {
|
||||
this.reportAppError({ kind: AppErrorKind.Validation, message: 'No results found for the selected types.', userMessage: 'No results found for the selected types.' });
|
||||
this.reportMdbError({ kind: MDBErrorKind.Validation, message: 'No results found for the selected types.', userMessage: 'No results found for the selected types.' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -121,12 +121,12 @@ export class MediaDbEntryHelper {
|
|||
|
||||
const apiSearchResults = await this.plugin.apiManager.query(advancedSearch.query, advancedSearch.apis);
|
||||
if (!apiSearchResults.ok) {
|
||||
this.reportAppError(apiSearchResults.error);
|
||||
this.reportMdbError(apiSearchResults.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!apiSearchResults.value.items || apiSearchResults.value.items.length === 0) {
|
||||
this.reportAppError({ kind: AppErrorKind.Validation, message: 'No results found.', userMessage: 'No results found.' });
|
||||
this.reportMdbError({ kind: MDBErrorKind.Validation, message: 'No results found.', userMessage: 'No results found.' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ export class MediaDbEntryHelper {
|
|||
|
||||
const queriedIdResult = await this.plugin.apiManager.queryDetailedInfoById(idSearchData.query, idSearchData.api);
|
||||
if (!queriedIdResult.ok) {
|
||||
this.reportAppError(queriedIdResult.error);
|
||||
this.reportMdbError(queriedIdResult.error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ export class MediaDbEntryHelper {
|
|||
|
||||
const createNoteResult = await this.plugin.fileHelper.createMediaDbNoteFromModel(idSearchResult, { attachTemplate: true, openNote: true });
|
||||
if (!createNoteResult.ok) {
|
||||
this.reportAppError(createNoteResult.error);
|
||||
this.reportMdbError(createNoteResult.error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ export class MediaDbEntryHelper {
|
|||
if (result.ok && result.value) {
|
||||
detailModels.push(result.value);
|
||||
} else if (!result.ok) {
|
||||
this.reportAppError(result.error);
|
||||
this.reportMdbError(result.error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ export class MediaDbEntryHelper {
|
|||
|
||||
const allSeasonsResult = await tmdbSeasonAPI.getSeasonsForSeries(seriesId);
|
||||
if (!allSeasonsResult.ok) {
|
||||
this.reportAppError(allSeasonsResult.error);
|
||||
this.reportMdbError(allSeasonsResult.error);
|
||||
new Notice(`Error loading seasons: ${allSeasonsResult.error.userMessage}`);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -276,7 +276,7 @@ export class MediaDbEntryHelper {
|
|||
if (seasonModel) {
|
||||
const fullMetadataResult = await tmdbSeasonAPI.getById(seasonModel.id);
|
||||
if (!fullMetadataResult.ok) {
|
||||
this.reportAppError(fullMetadataResult.error);
|
||||
this.reportMdbError(fullMetadataResult.error);
|
||||
new Notice(`Failed to load season ${selectedSeason.season_number}: ${fullMetadataResult.error.userMessage}`);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import { Notice, normalizePath, parseYaml, requestUrl, stringifyYaml } from 'obs
|
|||
import type MediaDbPlugin from 'packages/obsidian/src/main';
|
||||
import { ConfirmOverwriteModal } from 'packages/obsidian/src/modals/ConfirmOverwriteModal';
|
||||
import type { MediaTypeModel } from 'packages/obsidian/src/models/MediaTypeModel';
|
||||
import type { AppError } from 'packages/obsidian/src/utils/AppError';
|
||||
import { AppErrorKind, toAppError } from 'packages/obsidian/src/utils/AppError';
|
||||
import { Logger } from 'packages/obsidian/src/utils/Logger';
|
||||
import type { MDBError } from 'packages/obsidian/src/utils/MDBError';
|
||||
import { MDBErrorKind, toMdbError } from 'packages/obsidian/src/utils/MDBError';
|
||||
import type { MediaType } from 'packages/obsidian/src/utils/MediaType';
|
||||
import type { Result } from 'packages/obsidian/src/utils/result';
|
||||
import { err, ok } from 'packages/obsidian/src/utils/result';
|
||||
|
|
@ -29,7 +29,7 @@ export class MediaDbFileHelper {
|
|||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
async createMediaDbNotes(models: MediaTypeModel[], attachFile?: TFile): Promise<Result<void, AppError>> {
|
||||
async createMediaDbNotes(models: MediaTypeModel[], attachFile?: TFile): Promise<Result<void, MDBError>> {
|
||||
const results = await Promise.all(models.map(model => this.createMediaDbNoteFromModel(model, { attachTemplate: true, attachFile })));
|
||||
|
||||
const failures = results.filter(result => !result.ok);
|
||||
|
|
@ -45,7 +45,7 @@ export class MediaDbFileHelper {
|
|||
return ok(undefined);
|
||||
}
|
||||
|
||||
async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions): Promise<Result<void, AppError>> {
|
||||
async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions): Promise<Result<void, MDBError>> {
|
||||
Logger.debug('MDB | creating new note');
|
||||
|
||||
options.openNote = this.plugin.settings.openNoteInNewTab;
|
||||
|
|
@ -58,7 +58,7 @@ export class MediaDbFileHelper {
|
|||
}
|
||||
|
||||
const fileContentResult = await this.attempt(() => this.generateMediaDbNoteContents(mediaTypeModel, options), {
|
||||
kind: AppErrorKind.Unexpected,
|
||||
kind: MDBErrorKind.Unexpected,
|
||||
message: 'Failed to generate note contents',
|
||||
userMessage: 'Failed to generate note contents',
|
||||
});
|
||||
|
|
@ -67,7 +67,7 @@ export class MediaDbFileHelper {
|
|||
}
|
||||
|
||||
const folderResult = await this.attempt(() => this.plugin.mediaTypeManager.getFolder(mediaTypeModel, this.plugin.app), {
|
||||
kind: AppErrorKind.Vault,
|
||||
kind: MDBErrorKind.Vault,
|
||||
message: 'Failed to determine note folder',
|
||||
userMessage: 'Failed to determine note folder',
|
||||
});
|
||||
|
|
@ -84,7 +84,7 @@ export class MediaDbFileHelper {
|
|||
|
||||
if (this.plugin.settings.enableTemplaterIntegration) {
|
||||
const templaterResult = await this.attempt(() => useTemplaterPluginInFile(this.plugin.app, targetFileResult.value), {
|
||||
kind: AppErrorKind.Unexpected,
|
||||
kind: MDBErrorKind.Unexpected,
|
||||
message: 'Failed to apply templater to the note',
|
||||
userMessage: 'Failed to apply templater to the note',
|
||||
});
|
||||
|
|
@ -240,11 +240,11 @@ export class MediaDbFileHelper {
|
|||
return structuredClone(metadata ?? {});
|
||||
}
|
||||
|
||||
async createNote(fileName: string, fileContent: string, options: CreateNoteOptions): Promise<Result<TFile, AppError>> {
|
||||
async createNote(fileName: string, fileContent: string, options: CreateNoteOptions): Promise<Result<TFile, MDBError>> {
|
||||
const folder = options.folder ?? this.plugin.app.vault.getAbstractFileByPath('/');
|
||||
|
||||
if (!folder || !(folder instanceof TFolder)) {
|
||||
return err({ kind: AppErrorKind.Validation, message: 'MDB | invalid folder', userMessage: 'MDB | invalid folder' });
|
||||
return err({ kind: MDBErrorKind.Validation, message: 'MDB | invalid folder', userMessage: 'MDB | invalid folder' });
|
||||
}
|
||||
|
||||
fileName = replaceIllegalFileNameCharactersInString(fileName);
|
||||
|
|
@ -257,7 +257,7 @@ export class MediaDbFileHelper {
|
|||
});
|
||||
|
||||
if (!shouldOverwrite) {
|
||||
return err({ kind: AppErrorKind.Cancelled, message: 'MDB | file creation cancelled by user', userMessage: 'MDB | file creation cancelled by user' });
|
||||
return err({ kind: MDBErrorKind.Cancelled, message: 'MDB | file creation cancelled by user', userMessage: 'MDB | file creation cancelled by user' });
|
||||
}
|
||||
|
||||
await this.plugin.app.fileManager.trashFile(file);
|
||||
|
|
@ -278,7 +278,7 @@ export class MediaDbFileHelper {
|
|||
return ok(targetFile);
|
||||
}
|
||||
|
||||
private async downloadImageForMediaModel(mediaTypeModel: MediaTypeModel): Promise<Result<void, AppError>> {
|
||||
private async downloadImageForMediaModel(mediaTypeModel: MediaTypeModel): Promise<Result<void, MDBError>> {
|
||||
if (mediaTypeModel.image && typeof mediaTypeModel.image === 'string' && mediaTypeModel.image.startsWith('http')) {
|
||||
const imageUrl = mediaTypeModel.image;
|
||||
const imageResult = await this.attempt(
|
||||
|
|
@ -299,7 +299,7 @@ export class MediaDbFileHelper {
|
|||
mediaTypeModel.image = `[[${imagePath}]]`;
|
||||
},
|
||||
{
|
||||
kind: AppErrorKind.Network,
|
||||
kind: MDBErrorKind.Network,
|
||||
message: 'MDB | Failed to download image',
|
||||
userMessage: 'Failed to download image',
|
||||
},
|
||||
|
|
@ -314,12 +314,12 @@ export class MediaDbFileHelper {
|
|||
return ok(undefined);
|
||||
}
|
||||
|
||||
private async attempt<T>(operation: () => Promise<T> | T, fallback: Omit<AppError, 'cause'>): Promise<Result<T, AppError>> {
|
||||
private async attempt<T>(operation: () => Promise<T> | T, fallback: Omit<MDBError, 'cause'>): Promise<Result<T, MDBError>> {
|
||||
return await Promise.resolve()
|
||||
.then(operation)
|
||||
.then(
|
||||
value => ok(value),
|
||||
cause => err(toAppError(cause, fallback)),
|
||||
cause => err(toMdbError(cause, fallback)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import { MediaDbPreviewModal } from 'packages/obsidian/src/modals/MediaDbPreview
|
|||
import { MediaDbSearchModal } from 'packages/obsidian/src/modals/MediaDbSearchModal';
|
||||
import { MediaDbSearchResultModal } from 'packages/obsidian/src/modals/MediaDbSearchResultModal';
|
||||
import type { MediaTypeModel } from 'packages/obsidian/src/models/MediaTypeModel';
|
||||
import type { AppError } from 'packages/obsidian/src/utils/AppError';
|
||||
import { AppErrorKind, toAppError } from 'packages/obsidian/src/utils/AppError';
|
||||
import type { MDBError } from 'packages/obsidian/src/utils/MDBError';
|
||||
import { MDBErrorKind, toMdbError } from 'packages/obsidian/src/utils/MDBError';
|
||||
import type { MediaType } from 'packages/obsidian/src/utils/MediaType';
|
||||
import type { Outcome } from 'packages/obsidian/src/utils/result';
|
||||
import { cancelled, failure, OutcomeStatus, skipped, success } from 'packages/obsidian/src/utils/result';
|
||||
|
|
@ -106,7 +106,7 @@ export const SELECTMODALOPTIONSDEFAULT: SelectModalOptions = {
|
|||
};
|
||||
|
||||
interface ModalCoreResult<T, TModal> {
|
||||
modalResult: Outcome<T, AppError>;
|
||||
modalResult: Outcome<T, MDBError>;
|
||||
modal: TModal;
|
||||
}
|
||||
|
||||
|
|
@ -119,10 +119,10 @@ export class ModalHelper {
|
|||
|
||||
private async openModalCore<TData, TModal extends { open(): void; close(): void }>(
|
||||
createModal: () => TModal,
|
||||
wireHandlers: (modal: TModal, resolve: (result: Outcome<TData, AppError>) => void) => void,
|
||||
wireHandlers: (modal: TModal, resolve: (result: Outcome<TData, MDBError>) => void) => void,
|
||||
): Promise<ModalCoreResult<TData, TModal>> {
|
||||
const modal = createModal();
|
||||
const modalResult = await new Promise<Outcome<TData, AppError>>(resolve => {
|
||||
const modalResult = await new Promise<Outcome<TData, MDBError>>(resolve => {
|
||||
wireHandlers(modal, resolve);
|
||||
modal.open();
|
||||
});
|
||||
|
|
@ -130,7 +130,7 @@ export class ModalHelper {
|
|||
return { modalResult, modal };
|
||||
}
|
||||
|
||||
private async resolveOutcome<T>(outcomePromise: Promise<Outcome<T, AppError>>): Promise<T | undefined> {
|
||||
private async resolveOutcome<T>(outcomePromise: Promise<Outcome<T, MDBError>>): Promise<T | undefined> {
|
||||
const outcome = await outcomePromise;
|
||||
|
||||
if (outcome.status === OutcomeStatus.Ok) {
|
||||
|
|
@ -144,14 +144,14 @@ export class ModalHelper {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
async createSearchModalOutcome(searchModalOptions: SearchModalOptions): Promise<Outcome<SearchModalData, AppError>> {
|
||||
async createSearchModalOutcome(searchModalOptions: SearchModalOptions): Promise<Outcome<SearchModalData, MDBError>> {
|
||||
const { modalResult, modal } = await this.openModalCore<SearchModalData, MediaDbSearchModal>(
|
||||
() => new MediaDbSearchModal(this.plugin, searchModalOptions),
|
||||
(modal, resolve) => {
|
||||
modal.setSubmitCb(res => resolve(success(res)));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve(failure(toAppError(err, { kind: AppErrorKind.Modal, message: 'Search modal closed with an error' })));
|
||||
resolve(failure(toMdbError(err, { kind: MDBErrorKind.Modal, message: 'Search modal closed with an error' })));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -171,14 +171,14 @@ export class ModalHelper {
|
|||
return await this.resolveOutcome(this.createSearchModalOutcome(searchModalOptions));
|
||||
}
|
||||
|
||||
async createAdvancedSearchModalOutcome(advancedSearchModalOptions: AdvancedSearchModalOptions): Promise<Outcome<AdvancedSearchModalData, AppError>> {
|
||||
async createAdvancedSearchModalOutcome(advancedSearchModalOptions: AdvancedSearchModalOptions): Promise<Outcome<AdvancedSearchModalData, MDBError>> {
|
||||
const { modalResult, modal } = await this.openModalCore<AdvancedSearchModalData, MediaDbAdvancedSearchModal>(
|
||||
() => new MediaDbAdvancedSearchModal(this.plugin, advancedSearchModalOptions),
|
||||
(modal, resolve) => {
|
||||
modal.setSubmitCb(res => resolve(success(res)));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve(failure(toAppError(err, { kind: AppErrorKind.Modal, message: 'Advanced search modal closed with an error' })));
|
||||
resolve(failure(toMdbError(err, { kind: MDBErrorKind.Modal, message: 'Advanced search modal closed with an error' })));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -198,14 +198,14 @@ export class ModalHelper {
|
|||
return await this.resolveOutcome(this.createAdvancedSearchModalOutcome(advancedSearchModalOptions));
|
||||
}
|
||||
|
||||
async createIdSearchModalOutcome(idSearchModalOptions: IdSearchModalOptions): Promise<Outcome<IdSearchModalData, AppError>> {
|
||||
async createIdSearchModalOutcome(idSearchModalOptions: IdSearchModalOptions): Promise<Outcome<IdSearchModalData, MDBError>> {
|
||||
const { modalResult, modal } = await this.openModalCore<IdSearchModalData, MediaDbIdSearchModal>(
|
||||
() => new MediaDbIdSearchModal(this.plugin, idSearchModalOptions),
|
||||
(modal, resolve) => {
|
||||
modal.setSubmitCb(res => resolve(success(res)));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve(failure(toAppError(err, { kind: AppErrorKind.Modal, message: 'Id search modal closed with an error' })));
|
||||
resolve(failure(toMdbError(err, { kind: MDBErrorKind.Modal, message: 'Id search modal closed with an error' })));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ export class ModalHelper {
|
|||
return await this.resolveOutcome(this.createIdSearchModalOutcome(idSearchModalOptions));
|
||||
}
|
||||
|
||||
async createSelectModalOutcome(selectModalOptions: SelectModalOptions): Promise<Outcome<SelectModalData, AppError>> {
|
||||
async createSelectModalOutcome(selectModalOptions: SelectModalOptions): Promise<Outcome<SelectModalData, MDBError>> {
|
||||
const { modalResult, modal } = await this.openModalCore<SelectModalData, MediaDbSearchResultModal>(
|
||||
() => new MediaDbSearchResultModal(this.plugin, selectModalOptions),
|
||||
(modal, resolve) => {
|
||||
|
|
@ -233,7 +233,7 @@ export class ModalHelper {
|
|||
modal.setSkipCallback(() => resolve(skipped()));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve(failure(toAppError(err, { kind: AppErrorKind.Modal, message: 'Select modal closed with an error' })));
|
||||
resolve(failure(toMdbError(err, { kind: MDBErrorKind.Modal, message: 'Select modal closed with an error' })));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -253,14 +253,14 @@ export class ModalHelper {
|
|||
return await this.resolveOutcome(this.createSelectModalOutcome(selectModalOptions));
|
||||
}
|
||||
|
||||
async createPreviewModalOutcome(previewModalOptions: PreviewModalOptions): Promise<Outcome<PreviewModalData, AppError>> {
|
||||
async createPreviewModalOutcome(previewModalOptions: PreviewModalOptions): Promise<Outcome<PreviewModalData, MDBError>> {
|
||||
const { modalResult, modal } = await this.openModalCore<PreviewModalData, MediaDbPreviewModal>(
|
||||
() => new MediaDbPreviewModal(this.plugin, previewModalOptions),
|
||||
(modal, resolve) => {
|
||||
modal.setSubmitCb(res => resolve(success(res)));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve(failure(toAppError(err, { kind: AppErrorKind.Modal, message: 'Preview modal closed with an error' })));
|
||||
resolve(failure(toMdbError(err, { kind: MDBErrorKind.Modal, message: 'Preview modal closed with an error' })));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,30 +8,44 @@ export interface Err<E> {
|
|||
}
|
||||
export type Result<T, E> = Ok<T> | Err<E>;
|
||||
|
||||
export const ok = <T>(value: T): Ok<T> => ({ ok: true, value });
|
||||
export const err = <E>(error: E): Err<E> => ({ ok: false, error });
|
||||
export function ok<T>(value: T): Ok<T> {
|
||||
return { ok: true, value };
|
||||
}
|
||||
export function err<E>(error: E): Err<E> {
|
||||
return { ok: false, error };
|
||||
}
|
||||
|
||||
export const isOk = <T, E>(result: Result<T, E>): result is Ok<T> => result.ok;
|
||||
export const isErr = <T, E>(result: Result<T, E>): result is Err<E> => !result.ok;
|
||||
export function isOk<T, E>(result: Result<T, E>): result is Ok<T> {
|
||||
return result.ok;
|
||||
}
|
||||
export function isErr<T, E>(result: Result<T, E>): result is Err<E> {
|
||||
return !result.ok;
|
||||
}
|
||||
|
||||
export const mapResult = <T, E, U>(result: Result<T, E>, mapper: (value: T) => U): Result<U, E> => (result.ok ? ok(mapper(result.value)) : result);
|
||||
export function mapResult<T, E, U>(result: Result<T, E>, mapper: (value: T) => U): Result<U, E> {
|
||||
return result.ok ? ok(mapper(result.value)) : result;
|
||||
}
|
||||
|
||||
export const mapError = <T, E, F>(result: Result<T, E>, mapper: (error: E) => F): Result<T, F> => (result.ok ? result : err(mapper(result.error)));
|
||||
export function mapError<T, E, F>(result: Result<T, E>, mapper: (error: E) => F): Result<T, F> {
|
||||
return result.ok ? result : err(mapper(result.error));
|
||||
}
|
||||
|
||||
export const andThen = <T, E, U>(result: Result<T, E>, binder: (value: T) => Result<U, E>): Result<U, E> => (result.ok ? binder(result.value) : result);
|
||||
export function andThen<T, E, U>(result: Result<T, E>, binder: (value: T) => Result<U, E>): Result<U, E> {
|
||||
return result.ok ? binder(result.value) : result;
|
||||
}
|
||||
|
||||
export const tapError = <T, E>(result: Result<T, E>, sideEffect: (error: E) => void): Result<T, E> => {
|
||||
export function tapError<T, E>(result: Result<T, E>, sideEffect: (error: E) => void): Result<T, E> {
|
||||
if (!result.ok) sideEffect(result.error);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export const fromPromise = async <T, E>(promise: Promise<T>, onError: (cause: unknown) => E): Promise<Result<T, E>> => {
|
||||
export async function fromPromise<T, E>(promise: Promise<T>, onError: (cause: unknown) => E): Promise<Result<T, E>> {
|
||||
try {
|
||||
return ok(await promise);
|
||||
} catch (cause) {
|
||||
return err(onError(cause));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export enum OutcomeStatus {
|
||||
Ok = 'ok',
|
||||
|
|
@ -46,7 +60,15 @@ export type Outcome<T, E> =
|
|||
| { status: OutcomeStatus.Skipped }
|
||||
| { status: OutcomeStatus.Error; error: E };
|
||||
|
||||
export const cancelled = (): Outcome<never, never> => ({ status: OutcomeStatus.Cancelled });
|
||||
export const skipped = (): Outcome<never, never> => ({ status: OutcomeStatus.Skipped });
|
||||
export const success = <T>(data: T): Outcome<T, never> => ({ status: OutcomeStatus.Ok, data });
|
||||
export const failure = <E>(error: E): Outcome<never, E> => ({ status: OutcomeStatus.Error, error });
|
||||
export function cancelled(): Outcome<never, never> {
|
||||
return { status: OutcomeStatus.Cancelled };
|
||||
}
|
||||
export function skipped(): Outcome<never, never> {
|
||||
return { status: OutcomeStatus.Skipped };
|
||||
}
|
||||
export function success<T>(data: T): Outcome<T, never> {
|
||||
return { status: OutcomeStatus.Ok, data };
|
||||
}
|
||||
export function failure<E>(error: E): Outcome<never, E> {
|
||||
return { status: OutcomeStatus.Error, error };
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue