lib-result
Version:
A Rust-inspired `Result` type for type-safe error handling in TypeScript and JavaScript
327 lines (323 loc) • 6.82 kB
JavaScript
// src/utils.ts
function toError(e) {
if (e instanceof Error) return e;
if (typeof e === "string") return new Error(e);
if (isKeyValue(e)) return createCustomError(e);
return new Error("Unknown error");
}
function isKeyValue(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function createCustomError(props) {
const error = new Error(props?.message || "Unknown Error");
if (isKeyValue(props)) {
return Object.assign(error, props);
}
return error;
}
// src/result-methods.ts
function createWithAnd({ err }) {
return function withAnd(base) {
return Object.assign(base, {
and(result) {
if (this.isOk()) return result;
return err(this.error);
}
});
};
}
function createWithAndThen({ err }) {
return function withAndThen(base) {
return Object.assign(base, {
andThen(fn) {
try {
if (this.isOk()) return fn(this.ok);
return err(this.error);
} catch (e) {
throw toError(e);
}
}
});
};
}
function withExpect(base) {
return Object.assign(base, {
expect(message) {
if (this.isOk()) return this.ok;
throw createCustomError({
message,
cause: this.error
});
}
});
}
function withExpectErr(base) {
return Object.assign(base, {
expectErr(message) {
if (this.isError()) return this.error;
throw createCustomError({
message,
cause: this.error
});
}
});
}
function withInspect(base) {
return Object.assign(base, {
inspect(fn) {
if (this.isOk()) fn(this.ok);
return this;
}
});
}
function withInspectErr(base) {
return Object.assign(base, {
inspectErr(fn) {
if (this.isError()) fn(this.error);
return this;
}
});
}
function withIsError(base) {
return Object.assign(base, {
isError() {
return this.error !== void 0;
}
});
}
function withIsOk(base) {
return Object.assign(base, {
isOk() {
return this.error === void 0;
}
});
}
function createWithMap({ err, ok }) {
return function withMap(base) {
return Object.assign(base, {
map(fn) {
try {
if (this.isOk()) return ok(fn(this.ok));
return err(this.error);
} catch (e) {
throw toError(e);
}
}
});
};
}
function withMapOr(base) {
return Object.assign(base, {
mapOr(defaultValue, fn) {
try {
if (this.isError()) return defaultValue;
return fn(this.ok);
} catch (e) {
throw toError(e);
}
}
});
}
function withMapOrElse(base) {
return Object.assign(base, {
mapOrElse(defaultFn, fn) {
try {
if (this.isError()) return defaultFn(this.error);
return fn(this.ok);
} catch (e) {
throw toError(e);
}
}
});
}
function createWithMapErr({ err, ok }) {
return function withMapErr(base) {
return Object.assign(base, {
mapErr(fn) {
try {
if (this.isOk()) return ok(this.ok);
return err(fn(this.error));
} catch (e) {
throw toError(e);
}
}
});
};
}
function withMatch(base) {
return Object.assign(base, {
match(matchers) {
try {
return this.isError() ? matchers.errFn(this.error) : matchers.okFn(this.ok);
} catch (e) {
throw toError(e);
}
}
});
}
function createWithOr({ ok }) {
return function withOr(base) {
return Object.assign(base, {
or(result) {
if (this.isOk()) return ok(this.ok);
return result;
}
});
};
}
function createWithOrElse({ ok }) {
return function withOrElse(base) {
return Object.assign(base, {
orElse(fn) {
try {
if (this.isError()) return fn(this.error);
return ok(this.ok);
} catch (e) {
throw toError(e);
}
}
});
};
}
function withUnwrap(base) {
return Object.assign(base, {
unwrap() {
if (this.isOk()) return this.ok;
throw this.error;
}
});
}
function withUnwrapErr(base) {
return Object.assign(base, {
unwrapErr() {
if (this.isError()) return this.error;
throw new Error(`Received an Ok value '${this.ok}' instead of an Error`);
}
});
}
function withUnwrapOr(base) {
return Object.assign(base, {
unwrapOr(fallback) {
return this.isOk() ? this.ok : fallback;
}
});
}
function withUnwrapOrElse(base) {
return Object.assign(base, {
unwrapOrElse(fn) {
try {
if (this.isOk()) return this.ok;
return fn(this.error);
} catch (e) {
throw toError(e);
}
}
});
}
function createResultMethods({
err,
ok
}) {
return [
createWithAnd({ err }),
createWithAndThen({ err }),
withExpect,
withExpectErr,
withInspect,
withInspectErr,
withIsError,
withIsOk,
createWithMap({ err, ok }),
withMapOr,
withMapOrElse,
createWithMapErr({ err, ok }),
withMatch,
createWithOr({ ok }),
createWithOrElse({ ok }),
withUnwrap,
withUnwrapErr,
withUnwrapOr,
withUnwrapOrElse
];
}
// src/mixins.ts
function compose(base, ...fns) {
return Object.freeze(fns.reduce((obj, fn) => fn(obj), base));
}
function Ok(ok) {
return compose({ ok, error: void 0 }, ...methodsArray);
}
function Err(error) {
if (!(error instanceof Error)) {
throw new TypeError(
"Err() expects an Error instance, use ErrFromObject() instead."
);
}
return compose({ ok: void 0, error }, ...methodsArray);
}
var methodsArray = createResultMethods({ err: Err, ok: Ok });
function ErrFromUnknown(error) {
return compose(
{ ok: void 0, error: toError(error) },
...methodsArray
);
}
function ErrFromText(message) {
return compose(
{ ok: void 0, error: new Error(message) },
...methodsArray
);
}
function ErrFromObject(props) {
return compose(
{
ok: void 0,
error: createCustomError(props)
},
...methodsArray
);
}
// src/main.ts
function wrap(callback) {
try {
return Ok(callback());
} catch (error) {
return ErrFromUnknown(error);
}
}
async function wrapAsync(callback) {
try {
return Ok(await callback());
} catch (error) {
return ErrFromUnknown(error);
}
}
function wrapThrowable(callback) {
return (...args) => {
try {
return Ok(callback(...args));
} catch (error) {
return ErrFromUnknown(error);
}
};
}
function wrapAsyncThrowable(callback) {
return async (...args) => {
try {
return Ok(await callback(...args));
} catch (error) {
return ErrFromUnknown(error);
}
};
}
export {
Err,
ErrFromObject,
ErrFromText,
ErrFromUnknown,
Ok,
wrap,
wrapAsync,
wrapAsyncThrowable,
wrapThrowable
};