1import * as Equal from "../Equal.ts"
2import { format } from "../Formatter.ts"
3import { dual } from "../Function.ts"
4import * as Hash from "../Hash.ts"
5import { toJson } from "../Inspectable.ts"
6import type { Option } from "../Option.ts"
7import { hasProperty } from "../Predicate.ts"
8import type * as Result from "../Result.ts"
9import { SingleShotGen } from "../Utils.ts"
10import { PipeInspectableProto } from "./core.ts"
11import * as option from "./option.ts"
12
13const TypeId = "~effect/Result"
14
15const CommonProto = {
16 [TypeId]: {
17 /* v8 ignore next 2 */
18 _A: (_: never) => _,
19 _E: (_: never) => _
20 },
21 ...PipeInspectableProto,
22 [Symbol.iterator]() {
23 return new SingleShotGen(this)
24 }
25}
26
27const SuccessProto = Object.assign(Object.create(CommonProto), {
28 _tag: "Success",
29 _op: "Success",
30 [Equal.symbol]<A, E>(this: Result.Success<A, E>, that: unknown): boolean {
31 return (
32 isResult(that) && isSuccess(that) && Equal.equals(this.success, that.success)
33 )
34 },
35 [Hash.symbol]<A, E>(this: Result.Success<A, E>) {
36 return Hash.combine(Hash.hash(this._tag))(Hash.hash(this.success))
37 },
38 toString<A, E>(this: Result.Success<A, E>) {
39 return `success(${format(this.success)})`
40 },
41 toJSON<A, E>(this: Result.Success<A, E>) {
42 return {
43 _id: "Result",
44 _tag: this._tag,
45 value: toJson(this.success)
46 }
47 }
48})
49
50const FailureProto = Object.assign(Object.create(CommonProto), {
51 _tag: "Failure",
52 _op: "Failure",
53 [Equal.symbol]<A, E>(this: Result.Failure<A, E>, that: unknown): boolean {
54 return isResult(that) && isFailure(that) && Equal.equals(this.failure, that.failure)
55 },
56 [Hash.symbol]<A, E>(this: Result.Failure<A, E>) {
57 return Hash.combine(Hash.hash(this._tag))(Hash.hash(this.failure))
58 },
59 toString<A, E>(this: Result.Failure<A, E>) {
60 return `failure(${format(this.failure)})`
61 },
62 toJSON<A, E>(this: Result.Failure<A, E>) {
63 return {
64 _id: "Result",
65 _tag: this._tag,
66 failure: toJson(this.failure)
67 }
68 }
69})
70
71/** @internal */
72export const isResult = (input: unknown): input is Result.Result<unknown, unknown> => hasProperty(input, TypeId)
73
74/** @internal */
75export const isFailure = <A, E>(result: Result.Result<A, E>): result is Result.Failure<A, E> =>
76 result._tag === "Failure"
77
78/** @internal */
79export const isSuccess = <A, E>(result: Result.Result<A, E>): result is Result.Success<A, E> =>
80 result._tag === "Success"
81
82/** @internal */
83const FailureImpl = function(this: any, failure: unknown) {
84 this.failure = failure
85} as unknown as { new<E>(failure: E): Result.Result<never, E>; prototype: any }
86FailureImpl.prototype = FailureProto
87
88/** @internal */
89export const fail = <E>(failure: E): Result.Result<never, E> => new FailureImpl(failure)
90
91/** @internal */
92const SuccessImpl = function(this: any, success: unknown) {
93 this.success = success
94} as unknown as { new<A>(success: A): Result.Result<A>; prototype: any }
95SuccessImpl.prototype = SuccessProto
96
97/** @internal */
98export const succeed = <A>(success: A): Result.Result<A> => new SuccessImpl(success)
99
100/** @internal */
101export const getFailure = <A, E>(self: Result.Result<A, E>): Option<E> =>
102 isSuccess(self) ? option.none : option.some(self.failure)
103
104/** @internal */
105export const getSuccess = <A, E>(self: Result.Result<A, E>): Option<A> =>
106 isFailure(self) ? option.none : option.some(self.success)
107
108/** @internal */
109export const fromOption: {
110 <E>(onNone: () => E): <A>(self: Option<A>) => Result.Result<A, E>
111 <A, E>(self: Option<A>, onNone: () => E): Result.Result<A, E>
112} = dual(
113 2,
114 <A, E>(self: Option<A>, onNone: () => E): Result.Result<A, E> =>
115 option.isNone(self) ? fail(onNone()) : succeed(self.value)
116)