import * as Data from "effect/Data" import * as Effect from "effect/Effect" import * as Equal from "effect/Equal" import assert from "node:assert/strict" import { describe, it } from "vitest" describe("Data", () => { describe("Class", () => { class Person extends Data.Class<{ readonly name: string; readonly age: number }> {} it("assigns fields from constructor args", () => { const p = new Person({ name: "Alice", age: 30 }) assert.strictEqual(p.name, "Alice") assert.strictEqual(p.age, 30) }) it("structural equality via Equal.equals", () => { const a = new Person({ name: "Bob", age: 25 }) const b = new Person({ name: "Bob", age: 25 }) assert.strictEqual(Equal.equals(a, b), true) }) it("not equal when fields differ", () => { const a = new Person({ name: "Bob", age: 25 }) const b = new Person({ name: "Bob", age: 26 }) assert.strictEqual(Equal.equals(a, b), false) }) it("supports .pipe()", () => { const p = new Person({ name: "C", age: 1 }) const result = p.pipe((x) => x.name) assert.strictEqual(result, "C") }) it("allows classes without fields to be constructed without args", () => { class Empty extends Data.Class {} const e = new Empty() assert.ok(e instanceof Empty) }) it("fields are enumerable", () => { const p = new Person({ name: "D", age: 2 }) assert.deepStrictEqual(Object.keys(p).sort(), ["age", "name"]) }) }) describe("TaggedClass", () => { class Foo extends Data.TaggedClass("Foo")<{ readonly value: number }> {} it("sets _tag to the provided tag", () => { const f = new Foo({ value: 1 }) assert.strictEqual(f._tag, "Foo") }) it("assigns fields", () => { const f = new Foo({ value: 42 }) assert.strictEqual(f.value, 42) }) it("structural equality includes _tag", () => { const a = new Foo({ value: 1 }) const b = new Foo({ value: 1 }) assert.strictEqual(Equal.equals(a, b), true) }) it("different tags are not equal", () => { class Bar extends Data.TaggedClass("Bar")<{ readonly value: number }> {} const a = new Foo({ value: 1 }) const b = new Bar({ value: 1 }) assert.strictEqual(Equal.equals(a, b), false) }) it("allows tagged classes without fields to be constructed without args", () => { class Empty extends Data.TaggedClass("Empty") {} const e = new Empty() assert.strictEqual(e._tag, "Empty") }) it("supports .pipe()", () => { const f = new Foo({ value: 10 }) const result = f.pipe((x) => x._tag) assert.strictEqual(result, "Foo") }) }) describe("taggedEnum", () => { type Shape = Data.TaggedEnum<{ Circle: { readonly radius: number } Rect: { readonly width: number; readonly height: number } }> const Shape = Data.taggedEnum() describe("constructors", () => { it("creates a variant with _tag", () => { const c = Shape.Circle({ radius: 5 }) assert.strictEqual(c._tag, "Circle") assert.strictEqual(c.radius, 5) }) it("creates different variants", () => { const r = Shape.Rect({ width: 3, height: 4 }) assert.strictEqual(r._tag, "Rect") assert.strictEqual(r.width, 3) assert.strictEqual(r.height, 4) }) }) describe("$is", () => { it("returns true for matching variant", () => { const c = Shape.Circle({ radius: 1 }) assert.strictEqual(Shape.$is("Circle")(c), true) }) it("returns false for non-matching variant", () => { const r = Shape.Rect({ width: 1, height: 1 }) assert.strictEqual(Shape.$is("Circle")(r), false) }) it("returns false for non-tagged values", () => { assert.strictEqual(Shape.$is("Circle")(null), false) assert.strictEqual(Shape.$is("Circle")(undefined), false) assert.strictEqual(Shape.$is("Circle")("string"), false) assert.strictEqual(Shape.$is("Circle")({}), false) }) }) describe("$match", () => { const area = Shape.$match({ Circle: ({ radius }) => Math.PI * radius ** 2, Rect: ({ width, height }) => width * height }) it("data-last: matches Circle", () => { const c = Shape.Circle({ radius: 5 }) assert.strictEqual(area(c), Math.PI * 25) }) it("data-last: matches Rect", () => { const r = Shape.Rect({ width: 3, height: 4 }) assert.strictEqual(area(r), 12) }) it("data-first: matches directly", () => { const c = Shape.Circle({ radius: 1 }) const result = Shape.$match(c, { Circle: ({ radius }) => radius * 2, Rect: () => 0 }) assert.strictEqual(result, 2) }) }) describe("no-field variant", () => { type Option = Data.TaggedEnum<{ None: {} Some: { readonly value: number } }> const Option = Data.taggedEnum