import { extractMessageFromAnyError, MultiError } from '~/app/api/utils' const EMPTY_ARRAY = new Array(0) export function getEmptyArray() { return EMPTY_ARRAY } export function deepFilterRec( arr: Array, recKey: string, filterFn: (item: T) => boolean ): Array { return arr.reduce((acc, elem) => { if (!filterFn(elem)) return acc if (recKey in elem && elem[recKey as keyof T]) { const newSubitems = deepFilterRec(elem[recKey as keyof T] as Array, recKey, filterFn) const newElem = { ...elem, [recKey]: newSubitems } if (newSubitems.length > 0 || filterFn(elem)) acc.push(newElem) } else { acc.push(elem) } return acc }, [] as Array) } export async function pluckPromise(promise: Promise, key: K) { const data = await promise return data[key] } export class Result { constructor(private internal: { data: Ok; error: null } | { data: null; error: Error }) {} static ok(data: Ok): Result { return new Result({ data, error: null }) } static error(error: Error): Result { return new Result({ data: null, error }) } static tryCatchSync>( fn: (...args: Args) => Ok, onError: (error: unknown) => Error, ...args: Args ): Result { try { return Result.ok(fn(...args)) } catch (error: unknown) { return Result.error(onError(error)) } } static async tryCatch>( fn: (...args: Args) => Promise, onError: (error: unknown) => Error, ...args: Args ): Promise> { try { return Result.ok(await fn(...args)) } catch (error: unknown) { return Result.error(onError(error)) } } static async tryCatchFlat< Ok, Args extends Array = [], OuterError = unknown, InnerError = unknown, >( fn: (...args: Args) => Promise>, onError: (error: unknown) => OuterError | InnerError, ...args: Args ): Promise> { try { return await fn(...args) } catch (error: unknown) { return Result.error(onError(error)) } } static transposeArray( array: Array> ): Result, MultiError> { let data: Array = new Array(array.length) let error: MultiError | null = null for (const result of array) { if (result.isOk()) { data.push(result.internal.data!) } else { ;(error ??= new MultiError('MultiError:')).appendError( extractMessageFromAnyError(error), result.internal.error ) } } if (error) return Result.error(error) return Result.ok(data) } isOk(): this is Result { return this.internal.error == null } map(fn: (data: Ok) => Mapped): Result { if (this.isOk()) return Result.ok(fn(this.internal.data!)) return this as unknown as Result } async mapAsync(fn: (data: Ok) => Promise): Promise> { if (this.isOk()) return Result.ok(await fn(this.internal.data!)) return this as unknown as Result } mapError(fn: (error: Error) => MappedError): Result { if (this.isOk()) return this as unknown as Result return Result.error(fn(this.internal.error!)) } flatMap(fn: (data: Ok) => Result): Result { if (this.isOk()) return fn(this.internal.data!) return this as unknown as Result } flatMapAsync( fn: (data: Ok) => Promise> ): Promise> { if (this.isOk()) return fn(this.internal.data!) return Promise.resolve(this as unknown as Result) } match( onOk: (data: Ok) => Mapped, onError: (error: Error) => MappedError ): Mapped | MappedError { if (this.isOk()) return onOk(this.internal.data!) return onError(this.internal.error!) } unwrap(): Ok { if (!this.isOk()) { throw new Error(`Unwrap called on Err: ${this.internal.error}`, { cause: this.internal.error, }) } return this.internal.data! } unwrapOr(deflt: () => Ok): Ok { if (this.isOk()) return this.internal.data! return deflt() } unwrapError(): Error { if (this.isOk()) { throw new Error(`UnwrapError called on Ok`) } return this.internal.error! } unwrapErrorSafe(): Error | null { return this.internal.error } unwrapEither(): Ok | Error { if (this.isOk()) return this.unwrap() return this.unwrapError() } join( other: Result ): Result<[Ok, OtherOk], [Error, OtherError]> { if (!this.isOk() || !other.isOk()) return Result.error([this.internal.error, other.internal.error]) as Result< [Ok, OtherOk], [Error, OtherError] > return Result.ok([this.internal.data!, other.internal.data!]) } } export class Both { private internal: { left: Left right: Right } constructor(left: Left, right: Right) { this.internal = { left, right, } } mapLeft(fn: (left: Left) => NewLeft): Both { return new Both(fn(this.internal.left), this.internal.right) } mapRight(fn: (right: Right) => NewRight): Both { return new Both(this.internal.left, fn(this.internal.right)) } async mapLeftAsync(fn: (left: Left) => Promise): Promise> { const res = await fn(this.internal.left) return new Both(res, this.internal.right) } unwrapLeft(): Left { return this.internal.left } unwrapRight(): Right { return this.internal.right } combine(fn: (left: Left, right: Right) => Output): Output { return fn(this.internal.left, this.internal.right) } intoResult(): Result { if (this.internal.right) return Result.error(this.internal.right) return Result.ok(this.internal.left) } }