export async function tryDo<TResult>(promise: Promise<TResult>) : Promise<Option<{result: TResult}, {error: unknown}>> {
    try {
        return {ok: true, result: await promise};
    } catch(err) {
        return {ok: false, error: err};
    }
}

export async function assertOk<T>(promise: Promise<T>): Promise<T> {
    try {
        return await promise;
    } catch (err) {
        if (hasStackTrace(err)) {
            const trace: {stack?: string} = {};
            Error.captureStackTrace(trace);
            err.stack = `${err.stack}\nCaused by: ${trace.stack}`;
        }
        throw err;
    }
}

export function hasStackTrace(reason?: unknown): reason is {stack: unknown} {
    return reason && Object.prototype.hasOwnProperty.call(reason, "stack");
}

export function isHttpError<T>(err?: unknown): err is T {
    return err && Object.prototype.hasOwnProperty.call(err, "response");
}

export type Ok<T> = T & { ok: true };
export type Error<T> = T & { ok: false };
export type Option<T, U = unknown> = Ok<T> | Error<U>;