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