@jumpaku/async-result
Version:
A typescript library that provides Result, Option, and AsyncResult.
477 lines (472 loc) • 14 kB
JavaScript
import { BaseError } from 'make-error-cause';
class Some {
constructor(value) {
this.value = value;
this.length = 1;
this[0] = this.value;
this[Symbol.iterator] = () => {
const value = this.value;
return (function* () {
yield value;
})();
};
}
isSome() {
return true;
}
isNone() {
return false;
}
flatMap(f) {
return f(this.value);
}
map(f) {
return new Some(f(this.value));
}
orDefault(value) {
return this.value;
}
orBuild(f) {
return this.value;
}
orThrow(f) {
return this.value;
}
orNull() {
return this.value;
}
orUndefined() {
return this.value;
}
takeIf(f) {
return f(this.value) ? this : none();
}
takeIfNotNull() {
return nonNull(this.value);
}
ifPresent(f) {
f(this.value);
return this;
}
ifAbsent(f) {
return this;
}
and(other) {
return other;
}
or(other) {
return this;
}
}
class None {
constructor() {
this.length = 0;
this[Symbol.iterator] = () => (function* () { })();
}
isSome() {
return false;
}
isNone() {
return true;
}
flatMap(f) {
return None.instance;
}
map(f) {
return None.instance;
}
orDefault(value) {
return value;
}
orBuild(f) {
return f();
}
orThrow(f) {
throw f != null ? f() : new Error("Option is None.");
}
orNull() {
return null;
}
orUndefined() {
return undefined;
}
takeIf(f) {
return this;
}
takeIfNotNull() {
return None.instance;
}
ifPresent(f) {
return this;
}
ifAbsent(f) {
f();
return this;
}
and(other) {
return this;
}
or(other) {
return other;
}
}
None.instance = new None();
function none() {
return None.instance;
}
function some(value) {
return new Some(value);
}
function nonNull(nullable) {
return ((a) => a != null)(nullable)
? some(nullable)
: none();
}
class AsyncResult {
constructor(promise) {
this.promise = promise;
}
static of(result, catchFun) {
if (Result.isResult(result))
return new AsyncResult(Promise.resolve(result));
return catchFun == null
? new AsyncResult(result.catch((error) => Result.failure(error)))
: new AsyncResult(result.catch((error) => Result.failure(catchFun(error))));
}
static make(neverThrowExecutor) {
return AsyncResult.of(new Promise((resolve) => neverThrowExecutor((v) => resolve(Result.success(v)), (e) => resolve(Result.failure(e)))), (e) => e);
}
static success(v) {
return AsyncResult.of(Result.success(v));
}
static failure(e) {
return AsyncResult.of(Result.failure(e));
}
static try(tryFun, catchFun) {
const promise = new Promise((resolve, reject) => {
try {
resolve(tryFun());
}
catch (error) {
reject(error);
}
}).then(Result.success);
return catchFun == null
? AsyncResult.of(promise)
: AsyncResult.of(promise, catchFun);
}
then(onfulfilled, onrejected) {
return this.promise.then(onfulfilled, onrejected);
}
match(onSuccess, onFailure) {
return this.promise.then((result) => result.match(onSuccess, onFailure));
}
value() {
return this.promise.then((r) => r.value);
}
error() {
return this.promise.then((r) => r.error);
}
orNull() {
return this.promise.then((r) => r.orNull());
}
orUndefined() {
return this.promise.then((r) => r.orUndefined());
}
orReject(f) {
return this.promise.then((r) => (f == null ? r.orThrow() : r.orThrow(f)));
}
orDefault(value) {
return this.promise.then((r) => r.orDefault(value));
}
orRecover(neverThrowFun) {
return this.promise.then((r) => r.orRecover(neverThrowFun));
}
onSuccess(neverThrowFun) {
this.promise.then((r) => r.onSuccess(neverThrowFun));
return this;
}
onFailure(neverThrowFun) {
this.promise.then((r) => r.onFailure(neverThrowFun));
return this;
}
and(other) {
return new AsyncResult(this.promise.then((r0) => other.promise.then((r1) => r0.and(r1))));
}
or(other) {
return new AsyncResult(this.promise.then((r0) => other.promise.then((r1) => r0.or(r1))));
}
map(neverThrowFun) {
return new AsyncResult(this.promise.then((result) => result.map(neverThrowFun)));
}
tryMap(tryFun, catchFun) {
return (catchFun == null
? AsyncResult.try(() => this.promise.then((it) => it.tryMap(tryFun)))
: AsyncResult.try(() => this.promise.then((it) => it.tryMap(tryFun, catchFun)), catchFun)).flatMap((it) => AsyncResult.of(it));
}
flatMap(neverThrowFun) {
const promise = new Promise(async (resolve) => {
(await this.promise)
.onSuccess((value) => resolve(neverThrowFun(value).promise))
.onFailure((error) => resolve(Result.failure(error)));
});
return new AsyncResult(promise);
}
tryFlatMap(tryFun, catchFun) {
if (catchFun == null) {
const promise = this.promise
.then(async (r) => r.isFailure() ? r.castValue() : tryFun(r.value).promise)
.catch((error) => Result.failure(error));
return AsyncResult.of(promise);
}
else {
const promise = this.promise
.then(async (r) => r.isFailure() ? r.castValue() : tryFun(r.value).promise)
.catch((error) => Result.failure(catchFun(error)));
return AsyncResult.of(promise);
}
}
recover(neverThrowFun) {
return new AsyncResult(this.promise.then((result) => result.recover(neverThrowFun)));
}
tryRecover(tryFun, catchFun) {
return catchFun == null
? AsyncResult.of(this.match(Result.success, (e) => Result.try(() => tryFun(e))))
: AsyncResult.of(this.match(Result.success, (e) => Result.try(() => tryFun(e), catchFun)));
}
flatRecover(neverThrowFun) {
const promise = new Promise(async (resolve) => {
(await this.promise)
.onSuccess((value) => resolve(Result.success(value)))
.onFailure((error) => resolve(neverThrowFun(error).promise));
});
return new AsyncResult(promise);
}
tryFlatRecover(tryFun, catchFun) {
const promise = new Promise(async (resolve) => {
(await this.promise)
.onSuccess((value) => resolve(Result.success(value)))
.onFailure((error) => {
try {
resolve(tryFun(error).promise);
}
catch (e) {
if (catchFun == null)
resolve(Result.failure(e));
else
resolve(Result.failure(catchFun(e)));
}
});
});
return new AsyncResult(promise);
}
mapError(neverThrowFun) {
return new AsyncResult(this.promise.then((result) => result.mapError(neverThrowFun)));
}
tryMapError(tryFun, catchFun) {
return (catchFun == null
? AsyncResult.try(() => this.promise.then((it) => it.tryMapError(tryFun)))
: AsyncResult.try(() => this.promise.then((it) => it.tryMapError(tryFun, catchFun)), catchFun)).flatMap((it) => AsyncResult.of(it));
}
}
class ResultError extends BaseError {
constructor(error) {
super(error instanceof Error ? error.message : "", error instanceof Error ? error : undefined);
this.name = "ResultError";
this.detail = error;
}
}
const Result = {
success(value) {
return new Success(value);
},
failure(error) {
return new Failure(error);
},
try: (tryFun, catchFun) => {
try {
return Result.success(tryFun());
}
catch (error) {
return catchFun == null
? Result.failure(error)
: Result.failure(catchFun(error));
}
},
isResult(obj) {
return obj instanceof AbstractResult;
},
};
class AbstractResult {
assertsThisIsResult() {
if (!this.isSuccess() && !this.isFailure())
throw new Error();
}
match(onSuccess, onFailure) {
this.assertsThisIsResult();
return this.isSuccess() ? onSuccess(this.value) : onFailure(this.error);
}
orDefault(value) {
return this.isSuccess() ? this.value : value;
}
orRecover(f) {
this.assertsThisIsResult();
return this.isSuccess() ? this.value : f(this.error);
}
orThrow(f) {
this.assertsThisIsResult();
if (this.isFailure())
throw f != null ? f(this.error) : new ResultError(this.error);
return this.value;
}
orNull() {
return this.isSuccess() ? this.value : null;
}
orUndefined() {
return this.isSuccess() ? this.value : undefined;
}
onSuccess(f) {
if (this.isSuccess())
f(this.value);
this.assertsThisIsResult();
return this;
}
onFailure(f) {
this.assertsThisIsResult();
if (this.isFailure())
f(this.error);
return this;
}
and(other) {
return this.isFailure() ? this : other;
}
or(other) {
return this.isSuccess() ? this : other;
}
map(neverThrowFun) {
this.assertsThisIsResult();
return this.isSuccess()
? Result.success(neverThrowFun(this.value))
: this.castValue();
}
mapAsync(tryFun, catchFun) {
return catchFun == null
? this.match((v) => AsyncResult.try(() => tryFun(v)), (e) => AsyncResult.failure(e))
: this.match((v) => AsyncResult.try(() => tryFun(v), catchFun), (e) => AsyncResult.failure(e));
}
tryMap(tryFun, catchFun) {
return (catchFun == null
? Result.try(() => this.map(tryFun))
: Result.try(() => this.map(tryFun), catchFun)).flatMap((it) => it);
}
flatMap(neverThrowFun) {
this.assertsThisIsResult();
return this.isSuccess() ? neverThrowFun(this.value) : this.castValue();
}
flatMapAsync(tryFun, catchFun) {
this.assertsThisIsResult();
if (this.isFailure())
return AsyncResult.of(this.castValue());
try {
const result = tryFun(this.value);
return Result.isResult(result) ? AsyncResult.of(result) : result;
}
catch (error) {
return catchFun == null
? AsyncResult.failure(error)
: AsyncResult.failure(catchFun(error));
}
}
tryFlatMap(tryFun, catchFun) {
return (catchFun == null
? Result.try(() => this.flatMap(tryFun))
: Result.try(() => this.flatMap(tryFun), catchFun)).flatMap((it) => it);
}
recover(neverThrowFun) {
this.assertsThisIsResult();
return this.isFailure()
? Result.success(neverThrowFun(this.error))
: this.castError();
}
recoverAsync(tryFun, catchFun) {
this.assertsThisIsResult();
const f = async () => {
this.assertsThisIsResult();
return this.isSuccess() ? this.value : tryFun(this.error);
};
return catchFun == null ? AsyncResult.try(f) : AsyncResult.try(f, catchFun);
}
tryRecover(tryFun, catchFun) {
return (catchFun == null
? Result.try(() => this.recover(tryFun))
: Result.try(() => this.recover(tryFun), catchFun)).flatMap((it) => it);
}
flatRecover(neverThrowFun) {
this.assertsThisIsResult();
return this.isFailure() ? neverThrowFun(this.error) : this.castError();
}
flatRecoverAsync(tryFun, catchFun) {
this.assertsThisIsResult();
if (this.isSuccess())
return AsyncResult.of(this);
try {
const result = tryFun(this.error);
return Result.isResult(result) ? AsyncResult.of(result) : result;
}
catch (error) {
return catchFun == null
? AsyncResult.failure(error)
: AsyncResult.failure(catchFun(error));
}
}
tryFlatRecover(tryFun, catchFun) {
return (catchFun == null
? Result.try(() => this.flatRecover(tryFun))
: Result.try(() => this.flatRecover(tryFun), catchFun)).flatMap((it) => it);
}
mapError(neverThrowFun) {
this.assertsThisIsResult();
return this.isFailure()
? Result.failure(neverThrowFun(this.error))
: this.castError();
}
tryMapError(tryFun, catchFun) {
return (catchFun == null
? Result.try(() => this.mapError(tryFun))
: Result.try(() => this.mapError(tryFun), catchFun)).flatMap((it) => it);
}
}
class Success extends AbstractResult {
constructor(value) {
super();
this.value = value;
this.error = undefined;
}
isSuccess() {
return true;
}
isFailure() {
return false;
}
castError() {
return this;
}
}
class Failure extends AbstractResult {
constructor(error) {
super();
this.error = error;
this.value = undefined;
}
isSuccess() {
return false;
}
isFailure() {
return true;
}
castValue() {
return this;
}
}
export { AsyncResult, Failure, Result, ResultError, Success, nonNull, none, some };
//# sourceMappingURL=index.mjs.map