(input: {
readonly cwd: string
readonly args: string[]
readonly limit: number
readonly signal?: AbortSignal
readonly parse: (line: string) => Effect.Effect<A | undefined, Error>
readonly pattern?: string
readonly onItem?: (item: A) => Effect.Effect<void>
})
| 96 | const binary = yield* RipgrepBinary.Service |
| 97 | |
| 98 | const run = <A>(input: { |
| 99 | readonly cwd: string |
| 100 | readonly args: string[] |
| 101 | readonly limit: number |
| 102 | readonly signal?: AbortSignal |
| 103 | readonly parse: (line: string) => Effect.Effect<A | undefined, Error> |
| 104 | readonly pattern?: string |
| 105 | readonly onItem?: (item: A) => Effect.Effect<void> |
| 106 | }) => { |
| 107 | const program = Effect.scoped( |
| 108 | Effect.gen(function* () { |
| 109 | const handle = yield* process.spawn( |
| 110 | ChildProcess.make(yield* binary.filepath, input.args, { cwd: input.cwd, extendEnv: true, stdin: "ignore" }), |
| 111 | ) |
| 112 | const stderrFiber = yield* collectStream(handle.stderr, ERROR_BYTES).pipe( |
| 113 | Effect.map((output) => output.buffer.toString("utf8")), |
| 114 | Effect.forkScoped, |
| 115 | ) |
| 116 | let observed = 0 |
| 117 | const rows = yield* Stream.decodeText(handle.stdout).pipe( |
| 118 | Stream.splitLines, |
| 119 | Stream.filter((line) => line.length > 0), |
| 120 | Stream.mapEffect(input.parse), |
| 121 | Stream.filter((row): row is A => row !== undefined), |
| 122 | Stream.tap((row) => { |
| 123 | if (!input.onItem || observed++ >= input.limit) return Effect.void |
| 124 | return input.onItem(row) |
| 125 | }), |
| 126 | Stream.take(input.limit + 1), |
| 127 | Stream.runCollect, |
| 128 | Effect.map((chunk) => [...chunk]), |
| 129 | ) |
| 130 | const truncated = rows.length > input.limit |
| 131 | if (truncated) return { items: rows.slice(0, input.limit), truncated, partial: false } |
| 132 | |
| 133 | const code = yield* handle.exitCode |
| 134 | const stderr = yield* Fiber.join(stderrFiber) |
| 135 | if (input.pattern && code === 2 && isInvalidPattern(stderr)) { |
| 136 | return yield* new InvalidPatternError({ pattern: input.pattern, message: stderr.trim() }) |
| 137 | } |
| 138 | if (code !== 0 && code !== 1 && code !== 2) { |
| 139 | return yield* failure(stderr.trim() || `ripgrep failed with code ${code}`) |
| 140 | } |
| 141 | return { items: code === 1 ? [] : rows, truncated: false, partial: code === 2 } |
| 142 | }), |
| 143 | ) |
| 144 | const abortable = input.signal ? program.pipe(Effect.raceFirst(waitForAbort(input.signal))) : program |
| 145 | return abortable.pipe( |
| 146 | Effect.mapError((cause) => |
| 147 | cause instanceof Error || cause instanceof InvalidPatternError |
| 148 | ? cause |
| 149 | : failure("ripgrep execution failed", cause), |
| 150 | ), |
| 151 | ) |
| 152 | } |
| 153 | |
| 154 | return Service.of({ |
| 155 | glob: (input) => |
no test coverage detected