1/**
2 * Service contract for command-line arguments and standard input, output, and
3 * error output. It lets programs depend on standard I/O through the Effect
4 * environment instead of reading from or writing to global process handles
5 * directly.
6 *
7 * The service exposes arguments as an `Effect`, stdout and stderr as `Sink`s
8 * that accept strings or bytes, and stdin as a byte `Stream`. This module also
9 * provides a constructor for service values and a small test layer with
10 * overridable defaults.
11 *
12 * @since 4.0.0
13 */
14import * as Context from "./Context.ts"
15import * as Effect from "./Effect.ts"
16import * as Layer from "./Layer.ts"
17import type { PlatformError } from "./PlatformError.ts"
18import * as Sink from "./Sink.ts"
19import * as Stream from "./Stream.ts"
20
21/**
22 * String literal type used as the unique brand for the `Stdio` service.
23 *
24 * **When to use**
25 *
26 * Use to type the runtime identifier stored on `Stdio` service implementations.
27 *
28 * @category type IDs
29 * @since 4.0.0
30 */
31export type TypeId = "~effect/Stdio"
32
33/**
34 * Runtime identifier stored on `Stdio` service implementations.
35 *
36 * **Details**
37 *
38 * This marker is part of the runtime representation of `Stdio` service
39 * implementations.
40 *
41 * @category type IDs
42 * @since 4.0.0
43 */
44export const TypeId: TypeId = "~effect/Stdio"
45
46/**
47 * Defines the service interface for process standard I/O.
48 *
49 * **When to use**
50 *
51 * Use to depend on command-line arguments and standard I/O through the Effect
52 * environment.
53 *
54 * **Details**
55 *
56 * The service provides command-line arguments, sinks for standard output and
57 * standard error, and a stream of standard input bytes. I/O operations can fail
58 * with `PlatformError`.
59 *
60 * @category services
61 * @since 4.0.0
62 */
63export interface Stdio {
64 readonly [TypeId]: TypeId
65 readonly args: Effect.Effect<ReadonlyArray<string>>
66 /**
67 * Whether standard input is attached to a terminal.
68 *
69 * @since 4.0.0
70 */
71 readonly stdinIsTerminal: Effect.Effect<boolean>
72 /**
73 * Whether standard output is attached to a terminal.
74 *
75 * @since 4.0.0
76 */
77 readonly stdoutIsTerminal: Effect.Effect<boolean>
78 stdout(options?: {
79 readonly endOnDone?: boolean | undefined
80 }): Sink.Sink<void, string | Uint8Array, never, PlatformError>
81 stderr(options?: {
82 readonly endOnDone?: boolean | undefined
83 }): Sink.Sink<void, string | Uint8Array, never, PlatformError>
84 readonly stdin: Stream.Stream<Uint8Array, PlatformError>
85}
86/**
87 * Service tag for process standard I/O.
88 *
89 * **When to use**
90 *
91 * Use when you need command-line arguments or standard I/O streams supplied by
92 * an effect's environment.
93 *
94 * @see {@link make} for constructing a `Stdio` service directly
95 * @see {@link layerTest} for a test layer with defaults and overrides
96 *
97 * @category services
98 * @since 4.0.0
99 */
100export const Stdio: Context.Service<Stdio, Stdio> = Context.Service<Stdio>(TypeId)
101
102/**
103 * Creates a `Stdio` service implementation from the provided fields and
104 * attaches the `Stdio` type identifier.
105 *
106 * **When to use**
107 *
108 * Use when you need to assemble a concrete `Stdio` service from command-line
109 * arguments and standard I/O implementations.
110 *
111 * **Details**
112 *
113 * The returned service reuses the supplied fields unchanged and adds the
114 * `Stdio` type identifier. Omitted terminal-detection fields default to
115 * effects that succeed with `false`.
116 *
117 * @see {@link layerTest} for a test layer with default fields that can be overridden
118 *
119 * @category constructors
120 * @since 4.0.0
121 */
122export const make = (
123 options:
124 & Omit<Stdio, TypeId | "stdinIsTerminal" | "stdoutIsTerminal">
125 & Partial<Pick<Stdio, "stdinIsTerminal" | "stdoutIsTerminal">>
126): Stdio => ({
127 [TypeId]: TypeId,
128 stdinIsTerminal: Effect.succeed(false),
129 stdoutIsTerminal: Effect.succeed(false),
130 ...options
131})
132
133/**
134 * Creates a test layer for `Stdio`.
135 *
136 * **When to use**
137 *
138 * Use to provide deterministic standard I/O in tests while overriding only the
139 * command-line arguments, input stream, or output sinks relevant to the case.
140 *
141 * **Details**
142 *
143 * Any provided fields override defaults. By default, arguments are empty,
144 * standard output and error are draining sinks, and standard input is an empty
145 * stream, and terminal-detection effects succeed with `false`.
146 *
147 * @see {@link make} for constructing a `Stdio` service directly without a `Layer` or defaults
148 *
149 * @category layers
150 * @since 4.0.0
151 */
152export const layerTest = (impl: Partial<Stdio>): Layer.Layer<Stdio> =>
153 Layer.succeed(
154 Stdio,
155 make({
156 args: Effect.succeed([]),
157 stdout: () => Sink.drain,
158 stderr: () => Sink.drain,
159 stdin: Stream.empty,
160 ...impl
161 })
162 )