import { logger } from "../logger/index.js";
import { NEVER, ZodError, ZodIssueCode } from "zod/v3";
//#region lib/util/result.ts
function isZodResult(input) {
	if (typeof input !== "object" || input === null || Object.keys(input).length !== 2 || !("success" in input) || typeof input.success !== "boolean") return false;
	if (input.success) return "data" in input && typeof input.data !== "undefined" && input.data !== null;
	else return "error" in input && input.error instanceof ZodError;
}
function fromZodResult(input) {
	return input.success ? Result.ok(input.data) : Result.err(input.error);
}
function fromNullable(input, errForNull, errForUndefined) {
	if (input === null) return Result.err(errForNull);
	if (input === void 0) return Result.err(errForUndefined);
	return Result.ok(input);
}
/**
* Class for representing a result that can fail.
*
* The mental model:
* - `.wrap()` and `.wrapNullable()` are sinks
* - `.transform()` are pipes which can be chained
* - `.unwrap()` is the point of consumption
*/
var Result = class Result {
	res;
	constructor(res) {
		this.res = res;
	}
	static ok(val) {
		return new Result({
			ok: true,
			val
		});
	}
	static err(err) {
		return new Result({
			ok: false,
			err
		});
	}
	static _uncaught(err) {
		return new Result({
			ok: false,
			err,
			_uncaught: true
		});
	}
	static wrap(input) {
		if (isZodResult(input)) return fromZodResult(input);
		if (input instanceof Promise) return AsyncResult.wrap(input);
		try {
			const result = input();
			if (result instanceof Promise) return AsyncResult.wrap(result);
			return Result.ok(result);
		} catch (error) {
			return Result.err(error);
		}
	}
	static wrapNullable(input, arg2, arg3) {
		const errForNull = arg2;
		const errForUndefined = arg3 ?? arg2;
		if (input instanceof Promise) return AsyncResult.wrapNullable(input, errForNull, errForUndefined);
		if (input instanceof Function) try {
			return fromNullable(input(), errForNull, errForUndefined);
		} catch (error) {
			return Result.err(error);
		}
		return fromNullable(input, errForNull, errForUndefined);
	}
	/**
	* Returns a discriminated union for type-safe consumption of the result.
	* When error was uncaught during transformation, it's being re-thrown here.
	*
	*   ```ts
	*
	*   const { val, err } = Result.ok('foo').unwrap();
	*   expect(val).toBe('foo');
	*   expect(err).toBeUndefined();
	*
	*   ```
	*/
	unwrap() {
		if (this.res.ok) return this.res;
		if (this.res._uncaught) throw this.res.err;
		return this.res;
	}
	/**
	* Returns a success value or a fallback value.
	* When error was uncaught during transformation, it's being re-thrown here.
	*
	*   ```ts
	*
	*   const value = Result.err('bar').unwrapOr('foo');
	*   expect(val).toBe('foo');
	*
	*   ```
	*/
	unwrapOr(fallback) {
		if (this.res.ok) return this.res.val;
		if (this.res._uncaught) throw this.res.err;
		return fallback;
	}
	/**
	* Returns the ok-value or throw the error.
	*/
	unwrapOrThrow() {
		if (this.res.ok) return this.res.val;
		throw this.res.err;
	}
	/**
	* Returns the ok-value or `null`.
	* When error was uncaught during transformation, it's being re-thrown here.
	*/
	unwrapOrNull() {
		if (this.res.ok) return this.res.val;
		if (this.res._uncaught) throw this.res.err;
		return null;
	}
	transform(fn) {
		if (!this.res.ok) return Result.err(this.res.err);
		try {
			const result = fn(this.res.val);
			if (result instanceof Result) return result;
			if (result instanceof AsyncResult) return result;
			if (isZodResult(result)) return fromZodResult(result);
			if (result instanceof Promise) return AsyncResult.wrap(result, (err) => {
				logger.warn({ err }, "Result: unhandled async transform error");
				return Result._uncaught(err);
			});
			return Result.ok(result);
		} catch (err) {
			logger.warn({ err }, "Result: unhandled transform error");
			return Result._uncaught(err);
		}
	}
	catch(fn) {
		if (this.res.ok) return this;
		if (this.res._uncaught) return this;
		try {
			const result = fn(this.res.err);
			if (result instanceof Promise) return AsyncResult.wrap(result, (err) => {
				logger.warn({ err }, "Result: unexpected error in async catch handler");
				return Result._uncaught(err);
			});
			return result;
		} catch (err) {
			logger.warn({ err }, "Result: unexpected error in catch handler");
			return Result._uncaught(err);
		}
	}
	/**
	* Given a `schema` and `input`, returns a `Result` with `val` being the parsed value.
	* Additionally, `null` and `undefined` values are converted into Zod error.
	*/
	static parse(input, schema) {
		return fromZodResult(schema.transform((result, ctx) => {
			if (result === void 0) {
				ctx.addIssue({
					code: ZodIssueCode.custom,
					message: `Result can't accept nullish values, but input was parsed by Zod schema to undefined`
				});
				return NEVER;
			}
			if (result === null) {
				ctx.addIssue({
					code: ZodIssueCode.custom,
					message: `Result can't accept nullish values, but input was parsed by Zod schema to null`
				});
				return NEVER;
			}
			return result;
		}).safeParse(input));
	}
	/**
	* Given a `schema`, returns a `Result` with `val` being the parsed value.
	* Additionally, `null` and `undefined` values are converted into Zod error.
	*/
	parse(schema) {
		if (this.res.ok) return Result.parse(this.res.val, schema);
		const err = this.res.err;
		if (this.res._uncaught) return Result._uncaught(err);
		return Result.err(err);
	}
	/**
	* Call `fn` on the `val` if the result is ok.
	*/
	onValue(fn) {
		if (this.res.ok) try {
			fn(this.res.val);
		} catch (err) {
			return Result._uncaught(err);
		}
		return this;
	}
	/**
	* Call `fn` on the `err` if the result is err.
	*/
	onError(fn) {
		if (!this.res.ok) try {
			fn(this.res.err);
		} catch (err) {
			return Result._uncaught(err);
		}
		return this;
	}
};
/**
* This class is being used when `Result` methods encounter async code.
* It isn't meant to be used directly, but exported for usage in type annotations.
*
* All the methods resemble `Result` methods, but work asynchronously.
*/
var AsyncResult = class AsyncResult {
	asyncResult;
	constructor(asyncResult) {
		this.asyncResult = asyncResult;
	}
	then(onfulfilled) {
		return this.asyncResult.then(onfulfilled);
	}
	static ok(val) {
		return new AsyncResult(Promise.resolve(Result.ok(val)));
	}
	static err(err) {
		return new AsyncResult(Promise.resolve(Result.err(err)));
	}
	static wrap(promise, onErr) {
		return new AsyncResult(promise.then((value) => {
			if (value instanceof Result) return value;
			if (isZodResult(value)) return fromZodResult(value);
			return Result.ok(value);
		}).catch((err) => {
			if (onErr) return onErr(err);
			return Result.err(err);
		}));
	}
	static wrapNullable(promise, errForNull, errForUndefined) {
		return new AsyncResult(promise.then((value) => fromNullable(value, errForNull, errForUndefined)).catch((err) => Result.err(err)));
	}
	/**
	* Returns a discriminated union for type-safe consumption of the result.
	*
	*   ```ts
	*
	*   const { val, err } = await Result.wrap(readFile('foo.txt')).unwrap();
	*   expect(val).toBe('foo');
	*   expect(err).toBeUndefined();
	*
	*   ```
	*/
	unwrap() {
		return this.asyncResult.then((res) => res.unwrap());
	}
	/**
	* Returns a success value or a fallback value.
	*
	*   ```ts
	*
	*   const val = await Result.wrap(readFile('foo.txt')).unwrapOr('bar');
	*   expect(val).toBe('bar');
	*   expect(err).toBeUndefined();
	*
	*   ```
	*/
	unwrapOr(fallback) {
		return this.asyncResult.then((res) => res.unwrapOr(fallback));
	}
	/**
	* Returns the ok-value or throw the error.
	*/
	async unwrapOrThrow() {
		return (await this.asyncResult).unwrapOrThrow();
	}
	/**
	* Returns the ok-value or `null`.
	*/
	unwrapOrNull() {
		return this.asyncResult.then((res) => res.unwrapOrNull());
	}
	transform(fn) {
		return new AsyncResult(this.asyncResult.then((oldResult) => {
			const { ok, val: value, err: error } = oldResult.unwrap();
			if (!ok) return Result.err(error);
			try {
				const result = fn(value);
				if (result instanceof Result) return result;
				if (result instanceof AsyncResult) return result;
				if (isZodResult(result)) return fromZodResult(result);
				if (result instanceof Promise) return AsyncResult.wrap(result, (err) => {
					logger.warn({ err }, "AsyncResult: unhandled async transform error");
					return Result._uncaught(err);
				});
				return Result.ok(result);
			} catch (err) {
				logger.warn({ err }, "AsyncResult: unhandled transform error");
				return Result._uncaught(err);
			}
		}).catch((err) => {
			return Result._uncaught(err);
		}));
	}
	catch(fn) {
		const caughtAsyncResult = this.asyncResult.then((result) => result.catch(fn));
		return AsyncResult.wrap(caughtAsyncResult);
	}
	/**
	* Given a `schema`, returns a `Result` with `val` being the parsed value.
	* Additionally, `null` and `undefined` values are converted into Zod error.
	*/
	parse(schema) {
		return new AsyncResult(this.asyncResult.then((oldResult) => oldResult.parse(schema)).catch(
			/* istanbul ignore next: should never happen */
			(err) => Result._uncaught(err)
		));
	}
	onValue(fn) {
		return new AsyncResult(this.asyncResult.then((result) => result.onValue(fn)).catch(
			/* istanbul ignore next: should never happen */
			(err) => Result._uncaught(err)
		));
	}
	onError(fn) {
		return new AsyncResult(this.asyncResult.then((result) => result.onError(fn)).catch(
			/* istanbul ignore next: should never happen */
			(err) => Result._uncaught(err)
		));
	}
};
//#endregion
export { AsyncResult, Result };

//# sourceMappingURL=result.js.map