From 308025d66248c97766b16170ffa8458ab27d7066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 27 Oct 2025 11:33:15 +0100 Subject: [PATCH] Add Result type --- packages/effect-fc/src/Result.ts | 99 ++++++++++++++++++++ packages/effect-fc/src/SubscriptionSubRef.ts | 2 +- packages/effect-fc/src/index.ts | 1 + 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 packages/effect-fc/src/Result.ts diff --git a/packages/effect-fc/src/Result.ts b/packages/effect-fc/src/Result.ts new file mode 100644 index 0000000..1f8036d --- /dev/null +++ b/packages/effect-fc/src/Result.ts @@ -0,0 +1,99 @@ +import { Cause, Exit, Option, Pipeable, Predicate } from "effect" + + +export const TypeId: unique symbol = Symbol.for("@effect-fc/Result/Result") +export type TypeId = typeof TypeId + +export type Result = ( + | Initial + | Running

+ | Success + | (Success & Refreshing

) + | Failure + | (Failure & Refreshing

) +) + +export namespace Result { + export interface Prototype extends Pipeable.Pipeable { + readonly [TypeId]: TypeId + } + + export type Success> = [R] extends [Result] ? A : never + export type Failure> = [R] extends [Result] ? E : never + export type Progress> = [R] extends [Result] ? P : never +} + +export interface Initial extends Result.Prototype { + readonly _tag: "Initial" +} + +export interface Running

extends Result.Prototype { + readonly _tag: "Running" + readonly progress: P +} + +export interface Success extends Result.Prototype { + readonly _tag: "Success" + readonly value: A +} + +export interface Failure extends Result.Prototype { + readonly _tag: "Failure" + readonly cause: Cause.Cause + readonly previousSuccess: Option.Option> +} + +export interface Refreshing

{ + readonly refreshing: true + readonly progress: P +} + + +const ResultPrototype = Object.freeze({ + ...Pipeable.Prototype, + [TypeId]: TypeId, +} as const satisfies Result.Prototype) + + +export const isResult = (u: unknown): u is Result => Predicate.hasProperty(u, TypeId) +export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial" +export const isSuccess = (u: unknown): u is Success => isResult(u) && u._tag === "Success" +export const isFailure = (u: unknown): u is Failure => isResult(u) && u._tag === "Failure" +export const isRefreshing = (u: unknown): u is Refreshing => isResult(u) && Predicate.hasProperty(u, "refreshing") && u.refreshing + +export const initial = (): Initial => Object.setPrototypeOf({}, ResultPrototype) +export const running =

(progress?: P): Running

=> Object.setPrototypeOf({ progress }, ResultPrototype) +export const success = (value: A): Success => Object.setPrototypeOf({ value }, ResultPrototype) +export const failure = ( + cause: Cause.Cause, + previousSuccess?: Success, +): Failure => Object.setPrototypeOf({ + cause, + previousSuccess: Option.fromNullable(previousSuccess), +}, ResultPrototype) +export const refreshing = | Failure, P = never>( + result: R, + progress?: P, +): Omit>> & Refreshing

=> Object.setPrototypeOf( + Object.assign({}, result, { progress }), + Object.getPrototypeOf(result), +) + +export const fromExit = ( + exit: Exit.Exit +): Success | Failure => exit._tag === "Success" + ? success(exit.value) + : failure(exit.cause) + +export const toExit = ( + self: Result +): Exit.Exit => { + switch (self._tag) { + case "Success": + return Exit.succeed(self.value) + case "Failure": + return Exit.failCause(self.cause) + default: + return Exit.fail(new Cause.NoSuchElementException()) + } +} diff --git a/packages/effect-fc/src/SubscriptionSubRef.ts b/packages/effect-fc/src/SubscriptionSubRef.ts index 7980a29..95e8b0e 100644 --- a/packages/effect-fc/src/SubscriptionSubRef.ts +++ b/packages/effect-fc/src/SubscriptionSubRef.ts @@ -2,7 +2,7 @@ import { Chunk, Effect, Effectable, Option, Predicate, Readable, Ref, Stream, Su import * as PropertyPath from "./PropertyPath.js" -export const SubscriptionSubRefTypeId: unique symbol = Symbol.for("effect-fc/SubscriptionSubRef/SubscriptionSubRef") +export const SubscriptionSubRefTypeId: unique symbol = Symbol.for("@effect-fc/SubscriptionSubRef/SubscriptionSubRef") export type SubscriptionSubRefTypeId = typeof SubscriptionSubRefTypeId export interface SubscriptionSubRef> diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index f63afb4..2fd9c2c 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -4,6 +4,7 @@ export * as Form from "./Form.js" export * as Memoized from "./Memoized.js" export * as PropertyPath from "./PropertyPath.js" export * as ReactRuntime from "./ReactRuntime.js" +export * as Result from "./Result.js" export * as SetStateAction from "./SetStateAction.js" export * as Stream from "./Stream.js" export * as Subscribable from "./Subscribable.js"