From 8f39f500cbae0c11ed3fe8e96c0e9600d74953f2 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Mon, 10 Jul 2023 12:41:38 +0300 Subject: [PATCH] refactor: Don't restrict error type for `Result` (#23265) --- lib/modules/datasource/index.ts | 7 -- lib/util/result.spec.ts | 55 ++++++--------- lib/util/result.ts | 67 +++++++------------ .../repository/process/lookup/index.ts | 7 +- 4 files changed, 48 insertions(+), 88 deletions(-) diff --git a/lib/modules/datasource/index.ts b/lib/modules/datasource/index.ts index b0a5b91c573572..676e685d337ceb 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> { - 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 74d79451747e3d..c25d8c2049931c 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 ae6dcf917a0c7c..358c30fc8eef6d 100644 --- a/lib/util/result.ts +++ b/lib/util/result.ts @@ -1,33 +1,29 @@ interface Ok { - ok: true; - value: T; + readonly success: true; + readonly value: T; } -interface Err { - ok: false; - error: Error; +interface Err { + readonly success: false; + readonly error: E; } -type Res = Ok | Err; +type Res = Ok | Err; -export class Result { - static ok(value: T): Result { - return new Result({ ok: true, value }); +export class Result { + static ok(value: T): Result { + return new Result({ success: true, value }); } - static err(): Result; - static err(error: Error): Result; - static err(message: string): Result; - static err(error?: Error | string): Result { - if (typeof error === 'undefined') { - return new Result({ ok: false, error: new Error() }); + static err(): Result; + static err(e: E): Result; + static err(e?: E): Result | Result { + 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(callback: () => T): Result { @@ -55,36 +51,23 @@ export class Result { : Result.wrapCallback(input); } - private constructor(private res: Res) {} + private constructor(public readonly res: Res) {} - transform(fn: (value: T) => U): Result { - return this.res.ok + transform(fn: (value: T) => U): Result { + return this.res.success ? Result.ok(fn(this.res.value)) : Result.err(this.res.error); } - unwrap(): Res; - unwrap(fallback: U): Res; - unwrap(fallback?: U): Res { - if (this.res.ok) { - return this.res; - } - - if (arguments.length) { - return { ok: true, value: fallback as U }; - } - - return this.res; + catch(fallback: U): T | U { + return this.res.success ? this.res.value : fallback; } - value(): T | undefined; - value(fallback: U): T | U; - value(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 8c276343c4cbf1..744249060e42d3 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);