Refactor Result

This commit is contained in:
Julien Valverdé
2026-01-14 10:29:52 +01:00
parent 7705880afe
commit 931511b890

View File

@@ -1,4 +1,4 @@
import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Option, Pipeable, Predicate, PubSub, pipe, Ref, type Scope, Stream, Subscribable } from "effect"
import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Pipeable, Predicate, PubSub, pipe, Ref, type Scope, Stream, Subscribable } from "effect"
export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result")
@@ -12,7 +12,7 @@ export type Result<A, E = never, P = never> = (
)
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
export type Final<A, E = never, P = never> = (Success<A> | Failure<A, E>) & ({} | Flags<P>)
export type Final<A, E = never, P = never> = (Success<A> | Failure<E>) & ({} | Flags<P>)
export type Flags<P = never> = WillFetch | WillRefresh | Refreshing<P>
export declare namespace Result {
@@ -43,10 +43,9 @@ export interface Success<A> extends Result.Prototype {
readonly value: A
}
export interface Failure<A, E = never> extends Result.Prototype {
export interface Failure<E = never> extends Result.Prototype {
readonly _tag: "Failure"
readonly cause: Cause.Cause<E>
readonly previousSuccess: Option.Option<Success<A>>
}
export interface WillFetch {
@@ -76,7 +75,7 @@ const ResultPrototype = Object.freeze({
Match.tag("Initial", () => true),
Match.tag("Running", self => Equal.equals(self.progress, (that as Running<any>).progress)),
Match.tag("Success", self => Equal.equals(self.value, (that as Success<any>).value)),
Match.tag("Failure", self => Equal.equals(self.cause, (that as Failure<any, any>).cause)),
Match.tag("Failure", self => Equal.equals(self.cause, (that as Failure<any>).cause)),
Match.exhaustive,
)
},
@@ -105,7 +104,7 @@ export const isFinal = (u: unknown): u is Final<unknown, unknown, unknown> => is
export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial"
export const isRunning = (u: unknown): u is Running<unknown> => isResult(u) && u._tag === "Running"
export const isSuccess = (u: unknown): u is Success<unknown> => isResult(u) && u._tag === "Success"
export const isFailure = (u: unknown): u is Failure<unknown, unknown> => isResult(u) && u._tag === "Failure"
export const isFailure = (u: unknown): u is Failure<unknown> => isResult(u) && u._tag === "Failure"
export const hasFlag = (u: unknown): u is Flags => isResult(u) && Predicate.hasProperty(u, "_flag")
export const hasWillFetchFlag = (u: unknown): u is WillFetch => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillFetch"
export const hasWillRefreshFlag = (u: unknown): u is WillRefresh => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillRefresh"
@@ -117,15 +116,7 @@ export const initial: {
} = (): Initial => Object.setPrototypeOf({ _tag: "Initial" }, ResultPrototype)
export const running = <P = never>(progress?: P): Running<P> => Object.setPrototypeOf({ _tag: "Running", progress }, ResultPrototype)
export const succeed = <A>(value: A): Success<A> => Object.setPrototypeOf({ _tag: "Success", value }, ResultPrototype)
export const fail = <E, A = never>(
cause: Cause.Cause<E>,
previousSuccess?: Success<NoInfer<A>>,
): Failure<A, E> => Object.setPrototypeOf({
_tag: "Failure",
cause,
previousSuccess: Option.fromNullable(previousSuccess),
}, ResultPrototype)
export const fail = <E>(cause: Cause.Cause<E> ): Failure<E> => Object.setPrototypeOf({ _tag: "Failure", cause }, ResultPrototype)
export const willFetch = <R extends Initial | Final<any, any, any>>(
result: R
@@ -150,11 +141,8 @@ export const refreshing = <R extends Final<any, any, any>, P = never>(
)
export const fromExit = <A, E>(
exit: Exit.Exit<A, E>,
previousSuccess?: Success<NoInfer<A>>,
): Success<A> | Failure<A, E> => exit._tag === "Success"
? succeed(exit.value)
: fail(exit.cause, previousSuccess)
exit: Exit.Exit<A, E>
): Success<A> | Failure<E> => exit._tag === "Success" ? succeed(exit.value) : fail(exit.cause)
export const toExit = <A, E, P>(
self: Result<A, E, P>
@@ -219,7 +207,7 @@ export namespace unsafeForkEffect {
export interface Options<A, E, P> {
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
readonly initial?: (Initial | Success<A> | Failure<A, E>) & ({} | WillFetch | WillRefresh)
readonly initial?: (Initial | Success<A> | Failure<E>) & ({} | WillFetch | WillRefresh)
readonly initialProgress?: P
}
}
@@ -242,10 +230,7 @@ export const unsafeForkEffect = <A, E, R, P = never>(
).pipe(
Effect.andThen(effect),
Effect.onExit(exit => Effect.andThen(
state.set(fromExit(
exit,
isSuccess(options?.initial) ? options.initial : undefined),
),
state.set(fromExit(exit)),
Effect.forkScoped(PubSub.shutdown(pubsub)),
)),
)),