1/**
2 * @since 2.0.0
3 */
4import * as Equal from "../Equal.ts"
5import { format } from "../Formatter.ts"
6import * as Hash from "../Hash.ts"
7import { toJson } from "../Inspectable.ts"
8import type * as Option from "../Option.ts"
9import { hasProperty } from "../Predicate.ts"
10import { SingleShotGen } from "../Utils.ts"
11import { PipeInspectableProto } from "./core.ts"
12
13const TypeId = "~effect/Option"
14
15const CommonProto = {
16 [TypeId]: {
17 _A: (_: never) => _
18 },
19 ...PipeInspectableProto,
20 [Symbol.iterator]() {
21 return new SingleShotGen(this)
22 }
23}
24
25// `valueOrUndefined` is folded into the initializer (rather than a separate
26// `Object.defineProperty(SomeProto, ...)` statement) so the whole definition
27// is pure-annotated by the build and tree-shakable.
28const SomeProto = Object.defineProperty(
29 Object.assign(Object.create(CommonProto), {
30 _tag: "Some",
31 _op: "Some",
32 [Equal.symbol]<A>(this: Option.Some<A>, that: unknown): boolean {
33 return (
34 isOption(that) && isSome(that) && Equal.equals(this.value, that.value)
35 )
36 },
37 [Hash.symbol]<A>(this: Option.Some<A>) {
38 return Hash.combine(Hash.hash(this._tag))(Hash.hash(this.value))
39 },
40 toString<A>(this: Option.Some<A>) {
41 return `some(${format(this.value)})`
42 },
43 toJSON<A>(this: Option.Some<A>) {
44 return {
45 _id: "Option",
46 _tag: this._tag,
47 value: toJson(this.value)
48 }
49 }
50 }),
51 "valueOrUndefined",
52 {
53 get() {
54 return this.value
55 }
56 }
57)
58
59const NoneHash = Hash.hash("None")
60const NoneProto = Object.assign(Object.create(CommonProto), {
61 _tag: "None",
62 _op: "None",
63 valueOrUndefined: undefined,
64 [Equal.symbol]<A>(this: Option.None<A>, that: unknown): boolean {
65 return isOption(that) && isNone(that)
66 },
67 [Hash.symbol]<A>(this: Option.None<A>) {
68 return NoneHash
69 },
70 toString<A>(this: Option.None<A>) {
71 return `none()`
72 },
73 toJSON<A>(this: Option.None<A>) {
74 return {
75 _id: "Option",
76 _tag: this._tag
77 }
78 }
79})
80
81/** @internal */
82export const isOption = (input: unknown): input is Option.Option<unknown> => hasProperty(input, TypeId)
83
84/** @internal */
85export const isNone = <A>(fa: Option.Option<A>): fa is Option.None<A> => fa._tag === "None"
86
87/** @internal */
88export const isSome = <A>(fa: Option.Option<A>): fa is Option.Some<A> => fa._tag === "Some"
89
90/** @internal */
91export const none: Option.Option<never> = Object.create(NoneProto)
92
93/** @internal */
94const SomeImpl = function(this: any, value: unknown) {
95 this.value = value
96} as unknown as { new<A>(value: A): Option.Option<A>; prototype: any }
97SomeImpl.prototype = SomeProto
98
99/** @internal */
100export const some = <A>(value: A): Option.Option<A> => new SomeImpl(value)