1import * as NodeTerminal from "@effect/platform-node-shared/NodeTerminal"
2import { Prompt } from "effect/cli"
3import * as Effect from "effect/Effect"
4import * as Exit from "effect/Exit"
5import * as FileSystem from "effect/FileSystem"
6import * as Layer from "effect/Layer"
7import * as Option from "effect/Option"
8import * as Path from "effect/Path"
9import * as Queue from "effect/Queue"
10import * as Terminal from "effect/Terminal"
11
12const TerminalLayer = Layer.mergeAll(
13 NodeTerminal.layer,
14 FileSystem.layerNoop({}),
15 Path.layer
16)
17
18const prompts = Effect.gen(function*() {
19 const first = yield* Prompt.run(Prompt.Confirm({ message: "First" }))
20 const second = yield* Prompt.run(Prompt.Confirm({ message: "Second" })).pipe(Effect.flip)
21 return { first, second: second._tag }
22})
23
24const readInput = Effect.scoped(
25 Effect.gen(function*() {
26 const terminal = yield* Terminal.Terminal
27 const input = yield* terminal.readInput
28 const first = yield* Queue.take(input)
29 const second = yield* Queue.take(input)
30 const end = yield* Effect.exit(Queue.take(input))
31 return {
32 first: Option.getOrNull(first.input),
33 second: Option.getOrNull(second.input),
34 ended: Exit.isFailure(end)
35 }
36 })
37)
38
39const readLine = Effect.gen(function*() {
40 const terminal = yield* Terminal.Terminal
41 const first = yield* terminal.readLine
42 const second = yield* terminal.readLine.pipe(Effect.flip)
43 return { first, second: second._tag }
44})
45
46const readLines = Effect.gen(function*() {
47 const terminal = yield* Terminal.Terminal
48 const first = yield* terminal.readLine
49 const second = yield* terminal.readLine
50 return { first, second }
51})
52
53const unused = Effect.gen(function*() {
54 yield* Terminal.Terminal
55 return { dataListeners: process.stdin.listenerCount("data") }
56})
57
58const readLineAfterEnd = Effect.gen(function*() {
59 const terminal = yield* Terminal.Terminal
60 yield* Effect.callback<void>((resume) => {
61 if (process.stdin.readableEnded) {
62 resume(Effect.void)
63 return
64 }
65 const onEnd = () => resume(Effect.void)
66 process.stdin.once("end", onEnd)
67 process.stdin.resume()
68 return Effect.sync(() => process.stdin.off("end", onEnd))
69 })
70 const error = yield* terminal.readLine.pipe(Effect.flip)
71 return error._tag
72})
73
74const readLineDisposed = Effect.gen(function*() {
75 const terminal = yield* Terminal.Terminal
76 const line = yield* terminal.readLine
77 const duringTtl = process.stdin.listenerCount("data")
78 yield* Effect.sleep("20 millis")
79 return { line, duringTtl, dataListeners: process.stdin.listenerCount("data") }
80})
81
82const mode = process.argv[2]
83const program = Effect.gen(function*() {
84 if (mode === "prompts") {
85 return yield* prompts
86 } else if (mode === "read-input") {
87 return yield* readInput
88 } else if (mode === "read-line") {
89 return yield* readLine
90 } else if (mode === "read-lines") {
91 return yield* readLines
92 } else if (mode === "unused") {
93 return yield* unused
94 } else if (mode === "read-line-after-end") {
95 return yield* readLineAfterEnd
96 } else if (mode === "read-line-disposed") {
97 return yield* readLineDisposed
98 }
99 return yield* Effect.die(`Unknown mode: ${mode}`)
100})
101
102Effect.runPromise(program.pipe(Effect.provide(TerminalLayer))).then(
103 (result) => process.stderr.write(`RESULT ${JSON.stringify(result)}\n`),
104 (cause) => {
105 process.stderr.write(`ERROR ${String(cause)}\n`)
106 process.exitCode = 1
107 }
108)