diff --git a/lib/modules/datasource/index.ts b/lib/modules/datasource/index.ts index b0a5b91c5735723d0b3c0c01858457fc056762f3..676e685d337ceb557372fde3655a229fef1ce8ad 100644 --- a/lib/modules/datasource/index.ts +++ b/lib/modules/datasource/index.ts @@ -8,7 +8,6 @@ import * as packageCache from '../../util/cache/package'; import { clone } from '../../util/clone'; import { filterMap } from '../../util/filter-map'; import { regEx } from '../../util/regex'; -import { Result } from '../../util/result'; import { trimTrailingSlash } from '../../util/url'; import { defaultVersioning } from '../versioning'; import * as allVersioning from '../versioning'; @@ -506,12 +505,6 @@ export async function getPkgReleases( return res; } -export function getPkgReleasesSafe( - config: GetPkgReleasesConfig -): Promise<Result<ReleaseResult | null>> { - return Result.wrap(getPkgReleases(config)); -} - export function supportsDigests(datasource: string | undefined): boolean { const ds = !!datasource && getDatasourceFor(datasource); return !!ds && 'getDigest' in ds; diff --git a/lib/util/result.spec.ts b/lib/util/result.spec.ts index 74d79451747e3da94338651e6397e6f4dafb5661..c25d8c2049931c5bf1146d081650d078aa3b05db 100644 --- a/lib/util/result.spec.ts +++ b/lib/util/result.spec.ts @@ -3,50 +3,45 @@ import { Result } from './result'; describe('util/result', () => { describe('ok', () => { it('constructs successful result from value', () => { - expect(Result.ok(42).value()).toBe(42); + expect(Result.ok(42).value).toBe(42); }); }); describe('err', () => { - it('constructs error result', () => { + it('constructs `true` error by default', () => { const res = Result.err(); - expect(res.error()).toEqual(new Error()); - }); - - it('constructs error result from string', () => { - const res = Result.err('oops'); - expect(res.error()?.message).toBe('oops'); + expect(res.error).toBeTrue(); }); it('constructs error result from Error instance', () => { const err = new Error('oops'); const res = Result.err(err); - expect(res.error()).toBe(err); + expect(res.error).toBe(err); }); }); describe('wrap', () => { it('wraps function returning successful result', () => { const res = Result.wrap(() => 42); - expect(res.value()).toBe(42); + expect(res.value).toBe(42); }); it('wraps function that throws an error', () => { const res = Result.wrap(() => { throw new Error('oops'); }); - expect(res.error()?.message).toBe('oops'); + expect(res.error?.message).toBe('oops'); }); it('wraps promise resolving to value', async () => { const res = await Result.wrap(Promise.resolve(42)); - expect(res.value()).toBe(42); + expect(res.value).toBe(42); }); it('wraps promise rejecting with error', async () => { const err = new Error('oops'); const res = await Result.wrap(Promise.reject(err)); - expect(res.error()?.message).toBe('oops'); + expect(res.error?.message).toBe('oops'); }); }); @@ -61,59 +56,47 @@ describe('util/result', () => { it('no-op for error result', () => { const err = new Error('bar'); const res = Result.err(err).transform(fn); - expect(res.value()).toBeUndefined(); - expect(res.error()).toBe(err); + expect(res.value).toBeUndefined(); + expect(res.error).toBe(err); }); }); - describe('unwrap', () => { - it('unwraps successful result', () => { + describe('catch', () => { + it('returns original value for successful result', () => { const res = Result.ok(42); - expect(res.unwrap()).toEqual({ ok: true, value: 42 }); - }); - - it('unwraps error result with fallback value', () => { - const err = new Error('oops'); - const res = Result.err(err); - expect(res.unwrap(42)).toEqual({ ok: true, value: 42 }); + expect(res.catch(0)).toBe(42); }); - it('unwraps error result', () => { + it('returns fallback value for error result', () => { const err = new Error('oops'); const res = Result.err(err); - expect(res.unwrap()).toEqual({ ok: false, error: err }); + expect(res.catch(42)).toBe(42); }); }); describe('value', () => { it('returns successful value', () => { const res = Result.ok(42); - expect(res.value()).toBe(42); - }); - - it('returns fallback value for error result', () => { - const err = new Error('oops'); - const res = Result.err(err); - expect(res.value(42)).toBe(42); + expect(res.value).toBe(42); }); it('returns undefined value for error result', () => { const err = new Error('oops'); const res = Result.err(err); - expect(res.value()).toBeUndefined(); + expect(res.value).toBeUndefined(); }); }); describe('error', () => { it('returns undefined error for successful result', () => { const res = Result.ok(42); - expect(res.error()).toBeUndefined(); + expect(res.error).toBeUndefined(); }); it('returns error for non-successful result', () => { const err = new Error('oops'); const res = Result.err(err); - expect(res.error()).toEqual(err); + expect(res.error).toEqual(err); }); }); }); diff --git a/lib/util/result.ts b/lib/util/result.ts index ae6dcf917a0c7ca86500a3b8504d7ea350fb0bd5..358c30fc8eef6d172406466f8f97f7730d028cff 100644 --- a/lib/util/result.ts +++ b/lib/util/result.ts @@ -1,33 +1,29 @@ interface Ok<T> { - ok: true; - value: T; + readonly success: true; + readonly value: T; } -interface Err { - ok: false; - error: Error; +interface Err<E> { + readonly success: false; + readonly error: E; } -type Res<T> = Ok<T> | Err; +type Res<T, E> = Ok<T> | Err<E>; -export class Result<T> { - static ok<T>(value: T): Result<T> { - return new Result({ ok: true, value }); +export class Result<T, E = Error> { + static ok<T>(value: T): Result<T, never> { + return new Result({ success: true, value }); } - static err(): Result<never>; - static err(error: Error): Result<never>; - static err(message: string): Result<never>; - static err(error?: Error | string): Result<never> { - if (typeof error === 'undefined') { - return new Result({ ok: false, error: new Error() }); + static err(): Result<never, true>; + static err<E>(e: E): Result<never, E>; + static err<E>(e?: E): Result<never, E> | Result<never, true> { + if (typeof e === 'undefined' && arguments.length === 0) { + return new Result({ success: false, error: true }); } - if (typeof error === 'string') { - return new Result({ ok: false, error: new Error(error) }); - } - - return new Result({ ok: false, error }); + const error = e as E; + return new Result({ success: false, error }); } private static wrapCallback<T>(callback: () => T): Result<T> { @@ -55,36 +51,23 @@ export class Result<T> { : Result.wrapCallback(input); } - private constructor(private res: Res<T>) {} + private constructor(public readonly res: Res<T, E>) {} - transform<U>(fn: (value: T) => U): Result<U> { - return this.res.ok + transform<U>(fn: (value: T) => U): Result<U, E> { + return this.res.success ? Result.ok(fn(this.res.value)) : Result.err(this.res.error); } - unwrap(): Res<T>; - unwrap<U>(fallback: U): Res<T | U>; - unwrap<U>(fallback?: U): Res<T | U> { - if (this.res.ok) { - return this.res; - } - - if (arguments.length) { - return { ok: true, value: fallback as U }; - } - - return this.res; + catch<U>(fallback: U): T | U { + return this.res.success ? this.res.value : fallback; } - value(): T | undefined; - value<U>(fallback: U): T | U; - value<U>(fallback?: U): T | U | undefined { - const res = arguments.length ? this.unwrap(fallback as U) : this.unwrap(); - return res.ok ? res.value : undefined; + get value(): T | undefined { + return this.res.success ? this.res.value : undefined; } - error(): Error | undefined { - return this.res.ok ? undefined : this.res.error; + get error(): E | undefined { + return this.res.success ? undefined : this.res.error; } } diff --git a/lib/workers/repository/process/lookup/index.ts b/lib/workers/repository/process/lookup/index.ts index 8c276343c4cbf17ce332614fa87532feb947b6c1..744249060e42d35e5ed44e2b6763ecd650c4f605 100644 --- a/lib/workers/repository/process/lookup/index.ts +++ b/lib/workers/repository/process/lookup/index.ts @@ -9,7 +9,7 @@ import { getDatasourceList, getDefaultVersioning, getDigest, - getPkgReleasesSafe, + getPkgReleases, isGetPkgReleasesConfig, supportsDigests, } from '../../../../modules/datasource'; @@ -19,6 +19,7 @@ import { ExternalHostError } from '../../../../types/errors/external-host-error' import { clone } from '../../../../util/clone'; import { applyPackageRules } from '../../../../util/package-rules'; import { regEx } from '../../../../util/regex'; +import { Result } from '../../../../util/result'; import { getBucket } from './bucket'; import { getCurrentVersion } from './current'; import { filterVersions } from './filter'; @@ -82,8 +83,8 @@ export async function lookupUpdates( res.skipReason = 'is-pinned'; return res; } - const lookupResult = (await getPkgReleasesSafe(config)).unwrap(); - if (!lookupResult.ok) { + const { res: lookupResult } = await Result.wrap(getPkgReleases(config)); + if (!lookupResult.success) { throw lookupResult.error; } dependency = clone(lookupResult.value);