1/**
2 * Defines typed positional arguments for Effect CLI applications.
3 *
4 * Arguments consume ordered values after a command name and its flags, then
5 * parse them into the types a command handler expects. This module includes
6 * constructors for common argument shapes, plus helpers for optional or
7 * variadic arguments, schema validation, transformations, defaults, config
8 * fallbacks, and prompts for missing values.
9 *
10 * @stability unstable
11 * @since 4.0.0
12 */
13import type * as Config from "../Config.ts"
14import type * as Effect from "../Effect.ts"
15import { dual, type LazyArg } from "../Function.ts"
16import type * as Option from "../Option.ts"
17import type * as Redacted_ from "../Redacted.ts"
18import type * as Result from "../Result.ts"
19import type * as Schema from "../Schema.ts"
20import type * as CliError from "./CliError.ts"
21import type { Environment } from "./Command.ts"
22import * as Param from "./Param.ts"
23import type * as Primitive from "./Primitive.ts"
24
25// -------------------------------------------------------------------------------------
26// models
27// -------------------------------------------------------------------------------------
28
29/**
30 * Represents a positional command-line argument.
31 *
32 * **Gotchas**
33 *
34 * For booleans, use `Flag.Boolean` or `Argument.Literals` with "true" and "false".
35 *
36 * @stability unstable
37 * @category models
38 * @since 4.0.0
39 */
40export interface Argument<A> extends Param.Param<typeof Param.argumentKind, A> {}
41
42// -------------------------------------------------------------------------------------
43// constructors
44// -------------------------------------------------------------------------------------
45
46/**
47 * Creates a positional string argument.
48 *
49 * **Example** (Creating a string argument)
50 *
51 * ```ts import.meta.vitest
52 * import { Argument } from "effect/cli"
53 *
54 * const filename = Argument.String("filename")
55 * filename.kind // => "argument"
56 * ```
57 *
58 * @stability unstable
59 * @category constructors
60 * @since 4.0.0
61 */
62export const String = (name: string): Argument<string> => Param.String(Param.argumentKind, name)
63
64/**
65 * Creates a positional integer argument.
66 *
67 * **Example** (Creating an integer argument)
68 *
69 * ```ts import.meta.vitest
70 * import { Argument } from "effect/cli"
71 *
72 * const count = Argument.Int("count")
73 * count.kind // => "argument"
74 * ```
75 *
76 * @stability unstable
77 * @category constructors
78 * @since 4.0.0
79 */
80export const Int = (name: string): Argument<number> => Param.Int(Param.argumentKind, name)
81
82/**
83 * Creates a positional file path argument.
84 *
85 * **Example** (Creating file path arguments)
86 *
87 * ```ts import.meta.vitest
88 * import { Argument } from "effect/cli"
89 *
90 * const inputFile = Argument.File("input", { mustExist: true }) // Must exist
91 * const outputFile = Argument.File("output", { mustExist: false }) // Must not exist
92 * const kinds = [inputFile.kind, outputFile.kind] // => ["argument", "argument"]
93 * ```
94 *
95 * @stability unstable
96 * @category constructors
97 * @since 4.0.0
98 */
99export const File = (name: string, options?: {
100 readonly mustExist?: boolean | undefined
101}): Argument<string> => Param.File(Param.argumentKind, name, options)
102
103/**
104 * Creates a positional directory path argument.
105 *
106 * **Example** (Creating a directory path argument)
107 *
108 * ```ts import.meta.vitest
109 * import { Argument } from "effect/cli"
110 *
111 * const workspace = Argument.Directory("workspace", { mustExist: true }) // Must exist
112 * workspace.kind // => "argument"
113 * ```
114 *
115 * @stability unstable
116 * @category constructors
117 * @since 4.0.0
118 */
119export const Directory = (name: string, options?: {
120 readonly mustExist?: boolean | undefined
121}): Argument<string> => Param.Directory(Param.argumentKind, name, options)
122
123/**
124 * Creates a positional argument that parses finite numbers.
125 *
126 * **Example** (Parsing a finite number)
127 *
128 * ```ts import.meta.vitest
129 * import { Argument } from "effect/cli"
130 *
131 * const ratio = Argument.Finite("ratio")
132 * ratio.kind // => "argument"
133 * ```
134 *
135 * @stability unstable
136 * @category constructors
137 * @since 4.0.0
138 */
139export const Finite = (name: string): Argument<number> => Param.Finite(Param.argumentKind, name)
140
141/**
142 * Creates a positional date argument.
143 *
144 * **Example** (Creating a date argument)
145 *
146 * ```ts import.meta.vitest
147 * import { Argument } from "effect/cli"
148 *
149 * const startDate = Argument.Date("start-date")
150 * startDate.kind // => "argument"
151 * ```
152 *
153 * @stability unstable
154 * @category constructors
155 * @since 4.0.0
156 */
157export const Date = (name: string): Argument<globalThis.Date> => Param.Date(Param.argumentKind, name)
158
159/**
160 * Creates a positional choice argument.
161 *
162 * **Example** (Creating a choice argument)
163 *
164 * ```ts import.meta.vitest
165 * import { Argument } from "effect/cli"
166 *
167 * const environment = Argument.Literals("environment", ["dev", "staging", "prod"])
168 * environment.kind // => "argument"
169 * ```
170 *
171 * @stability unstable
172 * @category constructors
173 * @since 4.0.0
174 */
175export const Literals = <const Choices extends ReadonlyArray<string>>(
176 name: string,
177 choices: Choices
178): Argument<Choices[number]> => Param.Literals(Param.argumentKind, name, choices)
179
180/**
181 * Creates a positional path argument.
182 *
183 * **Example** (Creating a path argument)
184 *
185 * ```ts import.meta.vitest
186 * import { Argument } from "effect/cli"
187 *
188 * const configPath = Argument.Path("config")
189 * configPath.kind // => "argument"
190 * ```
191 *
192 * @stability unstable
193 * @category constructors
194 * @since 4.0.0
195 */
196export const Path = (name: string, options?: {
197 pathType?: "file" | "directory" | "either"
198 mustExist?: boolean
199}): Argument<string> => Param.Path(Param.argumentKind, name, options)
200
201/**
202 * Creates a positional redacted argument that obscures its value.
203 *
204 * **Example** (Creating a redacted argument)
205 *
206 * ```ts import.meta.vitest
207 * import { Argument } from "effect/cli"
208 *
209 * const secret = Argument.Redacted("secret")
210 * secret.kind // => "argument"
211 * ```
212 *
213 * @stability unstable
214 * @category constructors
215 * @since 4.0.0
216 */
217export const Redacted = (name: string): Argument<Redacted_.Redacted<string>> => Param.Redacted(Param.argumentKind, name)
218
219/**
220 * Creates a positional argument that reads file content as a string.
221 *
222 * **Example** (Reading file text)
223 *
224 * ```ts import.meta.vitest
225 * import { Argument } from "effect/cli"
226 *
227 * const config = Argument.FileText("config-file")
228 * config.kind // => "argument"
229 * ```
230 *
231 * @stability unstable
232 * @category constructors
233 * @since 4.0.0
234 */
235export const FileText = (name: string): Argument<string> => Param.FileText(Param.argumentKind, name)
236
237/**
238 * Creates a positional argument that reads a file and parses its content.
239 *
240 * **Details**
241 *
242 * The parser is chosen from the explicit `format` option or, when omitted, the
243 * file extension. The parsed value is `unknown`; use `fileSchema` when the
244 * parsed content should also be decoded with a Schema.
245 *
246 * **Example** (Parsing file content)
247 *
248 * ```ts import.meta.vitest
249 * import { Argument } from "effect/cli"
250 *
251 * const config = Argument.FileParse("config", { format: "json" })
252 * config.kind // => "argument"
253 * ```
254 *
255 * @stability unstable
256 * @category constructors
257 * @since 4.0.0
258 */
259export const FileParse = (
260 name: string,
261 options?: Primitive.FileParseOptions | undefined
262): Argument<unknown> => Param.FileParse(Param.argumentKind, name, options)
263
264/**
265 * Creates a positional argument that reads and validates file content using a schema.
266 *
267 * **Example** (Validating file content with a schema)
268 *
269 * ```ts import.meta.vitest
270 * import { Schema } from "effect"
271 * import { Argument } from "effect/cli"
272 *
273 * const ConfigSchema = Schema.Struct({
274 * port: Schema.Number,
275 * host: Schema.String
276 * })
277 *
278 * const config = Argument.FileSchema("config", ConfigSchema)
279 * config.kind // => "argument"
280 * ```
281 *
282 * @stability unstable
283 * @category constructors
284 * @since 4.0.0
285 */
286export const FileSchema = <A>(
287 name: string,
288 schema: Schema.ConstraintDecoder<A, Environment>,
289 options?: Primitive.FileSchemaOptions | undefined
290): Argument<A> => Param.FileSchema(Param.argumentKind, name, schema, options)
291
292/**
293 * An argument that always fails to parse.
294 *
295 * **Example** (Creating a sentinel argument)
296 *
297 * ```ts import.meta.vitest
298 * import { Argument } from "effect/cli"
299 *
300 * const noArg = Argument.Never
301 * noArg.kind // => "argument"
302 * ```
303 *
304 * @stability unstable
305 * @category constructors
306 * @since 4.0.0
307 */
308export const Never: Argument<never> = Param.Never(Param.argumentKind)
309
310// -------------------------------------------------------------------------------------
311// combinators
312// -------------------------------------------------------------------------------------
313
314/**
315 * Makes a positional argument optional.
316 *
317 * **Example** (Making an argument optional)
318 *
319 * ```ts import.meta.vitest
320 * import { Argument } from "effect/cli"
321 *
322 * const optionalVersion = Argument.String("version").pipe(Argument.optional)
323 * optionalVersion.kind // => "argument"
324 * ```
325 *
326 * @stability unstable
327 * @category combinators
328 * @since 4.0.0
329 */
330export const optional = <A>(arg: Argument<A>): Argument<Option.Option<A>> => Param.optional(arg)
331
332/**
333 * Adds a description to a positional argument.
334 *
335 * **Example** (Adding an argument description)
336 *
337 * ```ts import.meta.vitest
338 * import { Argument } from "effect/cli"
339 *
340 * const filename = Argument.String("filename").pipe(
341 * Argument.withDescription("The input file to process")
342 * )
343 * filename.kind // => "argument"
344 * ```
345 *
346 * @stability unstable
347 * @category combinators
348 * @since 4.0.0
349 */
350export const withDescription: {
351 <A>(description: string): (self: Argument<A>) => Argument<A>
352 <A>(self: Argument<A>, description: string): Argument<A>
353} = dual(2, <A>(self: Argument<A>, description: string) => Param.withDescription(self, description))
354
355/**
356 * Provides a default value for a positional argument.
357 *
358 * **Example** (Providing a default value)
359 *
360 * ```ts import.meta.vitest
361 * import { Argument } from "effect/cli"
362 *
363 * const port = Argument.Int("port").pipe(Argument.withDefault(8080))
364 * port.kind // => "argument"
365 * ```
366 *
367 * @stability unstable
368 * @category combinators
369 * @since 4.0.0
370 */
371export const withDefault: {
372 <const B>(
373 defaultValue: B | Effect.Effect<B, CliError.CliError, Environment>
374 ): <A>(self: Argument<A>) => Argument<A | B>
375 <A, const B>(
376 self: Argument<A>,
377 defaultValue: B | Effect.Effect<B, CliError.CliError, Environment>
378 ): Argument<A | B>
379} = Param.withDefault
380
381/**
382 * Adds a fallback config that is loaded when a required argument is missing.
383 *
384 * **Example** (Loading a fallback config)
385 *
386 * ```ts import.meta.vitest
387 * import { Config } from "effect"
388 * import { Argument } from "effect/cli"
389 *
390 * const repository = Argument.String("repository").pipe(
391 * Argument.withFallbackConfig(Config.String("REPOSITORY"))
392 * )
393 * repository.kind // => "argument"
394 * ```
395 *
396 * @stability unstable
397 * @category combinators
398 * @since 4.0.0
399 */
400export const withFallbackConfig: {
401 <B>(config: Config.Config<B>): <A>(self: Argument<A>) => Argument<A | B>
402 <A, B>(self: Argument<A>, config: Config.Config<B>): Argument<A | B>
403} = dual(2, <A, B>(self: Argument<A>, config: Config.Config<B>) => Param.withFallbackConfig(self, config))
404
405/**
406 * Adds a fallback prompt that is shown when a required argument is missing.
407 *
408 * **Example** (Showing a fallback prompt)
409 *
410 * ```ts import.meta.vitest
411 * import { Argument, Prompt } from "effect/cli"
412 *
413 * const filename = Argument.String("filename").pipe(
414 * Argument.withFallbackPrompt(Prompt.String({ message: "Filename" }))
415 * )
416 * filename.kind // => "argument"
417 * ```
418 *
419 * @stability unstable
420 * @category combinators
421 * @since 4.0.0
422 */
423export const withFallbackPrompt: {
424 <B>(prompt: Param.FallbackPrompt<B>): <A>(self: Argument<A>) => Argument<A | B>
425 <A, B>(self: Argument<A>, prompt: Param.FallbackPrompt<B>): Argument<A | B>
426} = dual(2, <A, B>(self: Argument<A>, prompt: Param.FallbackPrompt<B>) => Param.withFallbackPrompt(self, prompt))
427
428/**
429 * Creates a variadic positional argument that accepts multiple values.
430 *
431 * **Example** (Accepting multiple values)
432 *
433 * ```ts import.meta.vitest
434 * import { Argument } from "effect/cli"
435 *
436 * // Accept any number of files
437 * const anyFiles = Argument.String("files").pipe(Argument.variadic)
438 *
439 * // Accept at least 1 file
440 * const atLeastOneFile = Argument.String("files").pipe(
441 * Argument.variadic({ min: 1 })
442 * )
443 *
444 * // Accept between 1 and 5 files
445 * const limitedFiles = Argument.String("files").pipe(
446 * Argument.variadic({ min: 1, max: 5 })
447 * )
448 *
449 * const kinds = [anyFiles.kind, atLeastOneFile.kind, limitedFiles.kind] // => ["argument", "argument", "argument"]
450 * ```
451 *
452 * @stability unstable
453 * @category combinators
454 * @since 4.0.0
455 */
456export const variadic: {
457 (options?: Param.VariadicParamOptions | undefined): <A>(self: Argument<A>) => Argument<ReadonlyArray<A>>
458 <A>(self: Argument<A>, options?: Param.VariadicParamOptions | undefined): Argument<ReadonlyArray<A>>
459} = dual((args) => Param.isParam(args[0]), <A>(
460 self: Argument<A>,
461 options?: Param.VariadicParamOptions | undefined
462): Argument<ReadonlyArray<A>> => Param.variadic(self, options))
463
464/**
465 * Transforms the parsed value of a positional argument.
466 *
467 * **Example** (Mapping parsed values)
468 *
469 * ```ts import.meta.vitest
470 * import { Argument } from "effect/cli"
471 *
472 * const port = Argument.Int("port").pipe(
473 * Argument.map((p) => ({ port: p, url: `http://localhost:${p}` }))
474 * )
475 * port.kind // => "argument"
476 * ```
477 *
478 * @stability unstable
479 * @category combinators
480 * @since 4.0.0
481 */
482export const map: {
483 <A, B>(f: (a: A) => B): (self: Argument<A>) => Argument<B>
484 <A, B>(self: Argument<A>, f: (a: A) => B): Argument<B>
485} = dual(2, <A, B>(self: Argument<A>, f: (a: A) => B) => Param.map(self, f))
486
487/**
488 * Transforms the parsed value of a positional argument using an effectful function.
489 *
490 * **Example** (Validating values effectfully)
491 *
492 * ```ts import.meta.vitest
493 * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"
494 * import { Argument, CliError } from "effect/cli"
495 * import { ChildProcessSpawner } from "effect/process"
496 *
497 * const CliTestLayer = Layer.mergeAll(
498 * FileSystem.layerNoop({}),
499 * Path.layer,
500 * Stdio.layerTest({}),
501 * Layer.succeed(Terminal.Terminal, Terminal.make({
502 * columns: Effect.succeed(80),
503 * rows: Effect.succeed(24),
504 * readInput: Effect.die("unused"),
505 * readLine: Effect.die("unused"),
506 * display: () => Effect.void
507 * })),
508 * Layer.succeed(
509 * ChildProcessSpawner.ChildProcessSpawner,
510 * ChildProcessSpawner.make(() => Effect.die("unused"))
511 * )
512 * )
513 *
514 * const files = Argument.String("files").pipe(
515 * Argument.mapEffect((file) =>
516 * file.endsWith(".txt")
517 * ? Effect.succeed(file)
518 * : Effect.fail(
519 * new CliError.UserError({
520 * cause: new Error(`Unsupported file extension: ${file}`),
521 * userMessage: "Only .txt files allowed"
522 * })
523 * )
524 * )
525 * )
526 *
527 * const [, value] = await Effect.runPromise(
528 * files.parse({ arguments: ["notes.txt"], flags: {} }).pipe(Effect.provide(CliTestLayer))
529 * )
530 * value // => "notes.txt"
531 * ```
532 *
533 * @stability unstable
534 * @category combinators
535 * @since 4.0.0
536 */
537export const mapEffect: {
538 <A, B>(
539 f: (a: A) => Effect.Effect<B, CliError.CliError, Environment>
540 ): (self: Argument<A>) => Argument<B>
541 <A, B>(
542 self: Argument<A>,
543 f: (a: A) => Effect.Effect<B, CliError.CliError, Environment>
544 ): Argument<B>
545} = dual(2, <A, B>(
546 self: Argument<A>,
547 f: (a: A) => Effect.Effect<B, CliError.CliError, Environment>
548) => Param.mapEffect(self, f))
549
550/**
551 * Transforms the parsed value of a positional argument using a function that may throw.
552 *
553 * **Example** (Mapping values that may throw)
554 *
555 * ```ts import.meta.vitest
556 * import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"
557 * import { Argument } from "effect/cli"
558 * import { ChildProcessSpawner } from "effect/process"
559 *
560 * const CliTestLayer = Layer.mergeAll(
561 * FileSystem.layerNoop({}),
562 * Path.layer,
563 * Stdio.layerTest({}),
564 * Layer.succeed(Terminal.Terminal, Terminal.make({
565 * columns: Effect.succeed(80),
566 * rows: Effect.succeed(24),
567 * readInput: Effect.die("unused"),
568 * readLine: Effect.die("unused"),
569 * display: () => Effect.void
570 * })),
571 * Layer.succeed(
572 * ChildProcessSpawner.ChildProcessSpawner,
573 * ChildProcessSpawner.make(() => Effect.die("unused"))
574 * )
575 * )
576 *
577 * const json = Argument.String("data").pipe(
578 * Argument.mapTryCatch(
579 * (str) => JSON.parse(str),
580 * (error) =>
581 * `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`
582 * )
583 * )
584 *
585 * const [, value] = await Effect.runPromise(
586 * json.parse({ arguments: ['{"enabled":true}'], flags: {} }).pipe(Effect.provide(CliTestLayer))
587 * )
588 * value // => { enabled: true }
589 * ```
590 *
591 * @stability unstable
592 * @category combinators
593 * @since 4.0.0
594 */
595export const mapTryCatch: {
596 <A, B>(
597 f: (a: A) => B,
598 onError: (error: unknown) => string
599 ): (self: Argument<A>) => Argument<B>
600 <A, B>(self: Argument<A>, f: (a: A) => B, onError: (error: unknown) => string): Argument<B>
601} = dual(3, <A, B>(
602 self: Argument<A>,
603 f: (a: A) => B,
604 onError: (error: unknown) => string
605) => Param.mapTryCatch(self, f, onError))
606
607/**
608 * Creates a variadic argument that requires at least n values.
609 *
610 * **Example** (Requiring a minimum number of values)
611 *
612 * ```ts import.meta.vitest
613 * import { Argument } from "effect/cli"
614 *
615 * const files = Argument.String("files").pipe(Argument.atLeast(1))
616 * files.kind // => "argument"
617 * ```
618 *
619 * @stability unstable
620 * @category combinators
621 * @since 4.0.0
622 */
623export const atLeast: {
624 <A>(min: number): (self: Argument<A>) => Argument<ReadonlyArray<A>>
625 <A>(self: Argument<A>, min: number): Argument<ReadonlyArray<A>>
626} = dual(2, <A>(self: Argument<A>, min: number) => Param.atLeast(self, min))
627
628/**
629 * Creates a variadic argument that accepts at most n values.
630 *
631 * **Example** (Limiting the maximum number of values)
632 *
633 * ```ts import.meta.vitest
634 * import { Argument } from "effect/cli"
635 *
636 * const files = Argument.String("files").pipe(Argument.atMost(5))
637 * files.kind // => "argument"
638 * ```
639 *
640 * @stability unstable
641 * @category combinators
642 * @since 4.0.0
643 */
644export const atMost: {
645 <A>(max: number): (self: Argument<A>) => Argument<ReadonlyArray<A>>
646 <A>(self: Argument<A>, max: number): Argument<ReadonlyArray<A>>
647} = dual(2, <A>(self: Argument<A>, max: number) => Param.atMost(self, max))
648
649/**
650 * Creates a variadic argument that accepts between min and max values.
651 *
652 * **Example** (Requiring a range of values)
653 *
654 * ```ts import.meta.vitest
655 * import { Argument } from "effect/cli"
656 *
657 * const files = Argument.String("files").pipe(Argument.between(1, 5))
658 * files.kind // => "argument"
659 * ```
660 *
661 * @stability unstable
662 * @category combinators
663 * @since 4.0.0
664 */
665export const between: {
666 <A>(min: number, max: number): (self: Argument<A>) => Argument<ReadonlyArray<A>>
667 <A>(self: Argument<A>, min: number, max: number): Argument<ReadonlyArray<A>>
668} = dual(3, <A>(self: Argument<A>, min: number, max: number) => Param.between(self, min, max))
669
670/**
671 * Validates parsed values against a Schema.
672 *
673 * **Example** (Validating parsed values with a schema)
674 *
675 * ```ts import.meta.vitest
676 * import { Schema } from "effect"
677 * import { Argument } from "effect/cli"
678 *
679 * const input = Argument.String("input").pipe(
680 * Argument.withSchema(Schema.NonEmptyString)
681 * )
682 * input.kind // => "argument"
683 * ```
684 *
685 * @stability unstable
686 * @category combinators
687 * @since 4.0.0
688 */
689export const withSchema: {
690 <A, B>(schema: Schema.ConstraintCodec<B, A, Environment, unknown>): (self: Argument<A>) => Argument<B>
691 <A, B>(self: Argument<A>, schema: Schema.ConstraintCodec<B, A, Environment, unknown>): Argument<B>
692} = dual(
693 2,
694 <A, B>(self: Argument<A>, schema: Schema.ConstraintCodec<B, A, Environment, unknown>): Argument<B> =>
695 Param.withSchema(self, schema)
696)
697
698/**
699 * Creates a positional choice argument with custom value mapping.
700 *
701 * **Example** (Mapping choices to values)
702 *
703 * ```ts import.meta.vitest
704 * import { Argument } from "effect/cli"
705 *
706 * const logLevel = Argument.ChoiceWithValue("level", [
707 * ["debug", 0],
708 * ["info", 1],
709 * ["warn", 2],
710 * ["error", 3]
711 * ])
712 * logLevel.kind // => "argument"
713 * ```
714 *
715 * @stability unstable
716 * @category constructors
717 * @since 4.0.0
718 */
719export const ChoiceWithValue = <const Choices extends ReadonlyArray<readonly [string, any]>>(
720 name: string,
721 choices: Choices
722): Argument<Choices[number][1]> => Param.ChoiceWithValue(Param.argumentKind, name, choices)
723
724// -------------------------------------------------------------------------------------
725// metadata
726// -------------------------------------------------------------------------------------
727
728/**
729 * Sets a custom metavar (placeholder name) for the argument in help documentation.
730 *
731 * **Details**
732 *
733 * The metavar is displayed in usage text to indicate what value the user should provide.
734 * For example, `<FILE>` shows `FILE` as the metavar.
735 *
736 * **Example** (Setting a metavar)
737 *
738 * ```ts import.meta.vitest
739 * import { Argument } from "effect/cli"
740 *
741 * const port = Argument.Int("port").pipe(
742 * Argument.withMetavar("PORT")
743 * )
744 * port.kind // => "argument"
745 * ```
746 *
747 * @stability unstable
748 * @category metadata
749 * @since 4.0.0
750 */
751export const withMetavar: {
752 <A>(metavar: string): (self: Argument<A>) => Argument<A>
753 <A>(self: Argument<A>, metavar: string): Argument<A>
754} = dual(2, <A>(self: Argument<A>, metavar: string) => Param.withMetavar(self, metavar))
755
756/**
757 * Filters parsed values, failing with a custom error message if the predicate returns false.
758 *
759 * **Example** (Filtering parsed values)
760 *
761 * ```ts import.meta.vitest
762 * import { Argument } from "effect/cli"
763 *
764 * const positiveInt = Argument.Int("count").pipe(
765 * Argument.filter(
766 * (n) => n > 0,
767 * (n) => `Expected positive integer, got ${n}`
768 * )
769 * )
770 * positiveInt.kind // => "argument"
771 * ```
772 *
773 * @stability unstable
774 * @category combinators
775 * @since 4.0.0
776 */
777export const filter: {
778 <A>(predicate: (a: A) => boolean, onFalse: (a: A) => string): (self: Argument<A>) => Argument<A>
779 <A>(self: Argument<A>, predicate: (a: A) => boolean, onFalse: (a: A) => string): Argument<A>
780} = dual(3, <A>(
781 self: Argument<A>,
782 predicate: (a: A) => boolean,
783 onFalse: (a: A) => string
784) => Param.filter(self, predicate, onFalse))
785
786/**
787 * Filters and transforms parsed values, failing with a custom error message
788 * if the filter function returns None.
789 *
790 * **Example** (Filtering and mapping parsed values)
791 *
792 * ```ts import.meta.vitest
793 * import { Option } from "effect"
794 * import { Argument } from "effect/cli"
795 *
796 * const positiveInt = Argument.Int("count").pipe(
797 * Argument.filterMap(
798 * (n) => n > 0 ? Option.some(n) : Option.none(),
799 * (n) => `Expected positive integer, got ${n}`
800 * )
801 * )
802 * positiveInt.kind // => "argument"
803 * ```
804 *
805 * @stability unstable
806 * @category combinators
807 * @since 4.0.0
808 */
809export const filterMap: {
810 <A, B>(f: (a: A) => Option.Option<B>, onNone: (a: A) => string): (self: Argument<A>) => Argument<B>
811 <A, B>(self: Argument<A>, f: (a: A) => Option.Option<B>, onNone: (a: A) => string): Argument<B>
812} = dual(3, <A, B>(
813 self: Argument<A>,
814 f: (a: A) => Option.Option<B>,
815 onNone: (a: A) => string
816) => Param.filterMap(self, f, onNone))
817
818/**
819 * Provides a fallback argument to use if this argument fails to parse.
820 *
821 * **Example** (Providing a fallback argument)
822 *
823 * ```ts import.meta.vitest
824 * import { Argument } from "effect/cli"
825 *
826 * const value = Argument.Int("value").pipe(
827 * Argument.orElse(() => Argument.String("value"))
828 * )
829 * value.kind // => "argument"
830 * ```
831 *
832 * @stability unstable
833 * @category combinators
834 * @since 4.0.0
835 */
836export const orElse: {
837 <B>(that: LazyArg<Argument<B>>): <A>(self: Argument<A>) => Argument<A | B>
838 <A, B>(self: Argument<A>, that: LazyArg<Argument<B>>): Argument<A | B>
839} = dual(2, <A, B>(self: Argument<A>, that: LazyArg<Argument<B>>) => Param.orElse(self, that))
840
841/**
842 * Provides a fallback argument, wrapping results in Result to distinguish which succeeded.
843 *
844 * **Example** (Returning which fallback succeeded)
845 *
846 * ```ts import.meta.vitest
847 * import { Argument } from "effect/cli"
848 *
849 * const source = Argument.File("source").pipe(
850 * Argument.orElseResult(() => Argument.String("url"))
851 * )
852 * // Returns Result<string, string>
853 * source.kind // => "argument"
854 * ```
855 *
856 * @stability unstable
857 * @category combinators
858 * @since 4.0.0
859 */
860export const orElseResult: {
861 <B>(that: LazyArg<Argument<B>>): <A>(self: Argument<A>) => Argument<Result.Result<A, B>>
862 <A, B>(self: Argument<A>, that: LazyArg<Argument<B>>): Argument<Result.Result<A, B>>
863} = dual(2, <A, B>(self: Argument<A>, that: LazyArg<Argument<B>>) => Param.orElseResult(self, that))