Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
Implement support for variadic Options (#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
IMax153 authored Nov 20, 2023
1 parent d24623b commit 714fe74
Show file tree
Hide file tree
Showing 8 changed files with 477 additions and 109 deletions.
5 changes: 5 additions & 0 deletions .changeset/odd-eels-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/cli": patch
---

add support for variadic options
7 changes: 0 additions & 7 deletions src/Args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,6 @@ export const path: (config?: Args.PathArgsConfig) => Args<string> = InternalArgs
*/
export const repeated: <A>(self: Args<A>) => Args<ReadonlyArray<A>> = InternalArgs.repeated

/**
* @since 1.0.0
* @category combinators
*/
export const repeatedAtLeastOnce: <A>(self: Args<A>) => Args<NonEmptyReadonlyArray<A>> =
InternalArgs.repeatedAtLeastOnce

/**
* Creates a text argument.
*
Expand Down
37 changes: 37 additions & 0 deletions src/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,37 @@ export const text: (name: string) => Options<string> = InternalOptions.text
// Combinators
// =============================================================================

/**
* @since 1.0.0
* @category combinators
*/
export const atMost: {
(times: number): <A>(self: Options<A>) => Options<ReadonlyArray<A>>
<A>(self: Options<A>, times: number): Options<ReadonlyArray<A>>
} = InternalOptions.atMost

/**
* @since 1.0.0
* @category combinators
*/
export const atLeast: {
(times: 0): <A>(self: Options<A>) => Options<ReadonlyArray<A>>
(times: number): <A>(self: Options<A>) => Options<NonEmptyReadonlyArray<A>>
<A>(self: Options<A>, times: 0): Options<ReadonlyArray<A>>
<A>(self: Options<A>, times: number): Options<readonly [A, ...Array<A>]>
} = InternalOptions.atLeast

/**
* @since 1.0.0
* @category combinators
*/
export const between: {
(min: 0, max: number): <A>(self: Options<A>) => Options<ReadonlyArray<A>>
(min: number, max: number): <A>(self: Options<A>) => Options<NonEmptyReadonlyArray<A>>
<A>(self: Options<A>, min: 0, max: number): Options<ReadonlyArray<A>>
<A>(self: Options<A>, min: number, max: number): Options<NonEmptyReadonlyArray<A>>
} = InternalOptions.between

/**
* @since 1.0.0
* @category combinators
Expand Down Expand Up @@ -361,6 +392,12 @@ export const parse: {
): Effect<FileSystem, ValidationError, A>
} = InternalOptions.parse

/**
* @since 1.0.0
* @category combinators
*/
export const repeated: <A>(self: Options<A>) => Options<ReadonlyArray<A>> = InternalOptions.repeated

/**
* Returns a `RegularLanguage` whose accepted language is equivalent to the
* language accepted by the provided `Options`.
Expand Down
37 changes: 23 additions & 14 deletions src/ValidationError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export type ValidationError =
| CorrectedFlag
| InvalidArgument
| InvalidValue
| KeyValuesDetected
| MissingValue
| MissingFlag
| MultipleValuesDetected
| MissingSubcommand
| NoBuiltInMatch
| UnclusteredFlag
Expand All @@ -38,6 +38,7 @@ export type ValidationError =
*/
export interface CommandMismatch extends ValidationError.Proto {
readonly _tag: "CommandMismatch"
readonly error: HelpDoc
}

/**
Expand All @@ -46,6 +47,7 @@ export interface CommandMismatch extends ValidationError.Proto {
*/
export interface CorrectedFlag extends ValidationError.Proto {
readonly _tag: "CorrectedFlag"
readonly error: HelpDoc
}

/**
Expand All @@ -54,6 +56,7 @@ export interface CorrectedFlag extends ValidationError.Proto {
*/
export interface InvalidArgument extends ValidationError.Proto {
readonly _tag: "InvalidArgument"
readonly error: HelpDoc
}

/**
Expand All @@ -62,15 +65,7 @@ export interface InvalidArgument extends ValidationError.Proto {
*/
export interface InvalidValue extends ValidationError.Proto {
readonly _tag: "InvalidValue"
}

/**
* @since 1.0.0
* @category models
*/
export interface KeyValuesDetected extends ValidationError.Proto {
readonly _tag: "KeyValuesDetected"
readonly keyValues: ReadonlyArray<string>
readonly error: HelpDoc
}

/**
Expand All @@ -79,6 +74,7 @@ export interface KeyValuesDetected extends ValidationError.Proto {
*/
export interface MissingFlag extends ValidationError.Proto {
readonly _tag: "MissingFlag"
readonly error: HelpDoc
}

/**
Expand All @@ -87,6 +83,7 @@ export interface MissingFlag extends ValidationError.Proto {
*/
export interface MissingValue extends ValidationError.Proto {
readonly _tag: "MissingValue"
readonly error: HelpDoc
}

/**
Expand All @@ -95,6 +92,17 @@ export interface MissingValue extends ValidationError.Proto {
*/
export interface MissingSubcommand extends ValidationError.Proto {
readonly _tag: "MissingSubcommand"
readonly error: HelpDoc
}

/**
* @since 1.0.0
* @category models
*/
export interface MultipleValuesDetected extends ValidationError.Proto {
readonly _tag: "MultipleValuesDetected"
readonly error: HelpDoc
readonly values: ReadonlyArray<string>
}

/**
Expand All @@ -103,6 +111,7 @@ export interface MissingSubcommand extends ValidationError.Proto {
*/
export interface NoBuiltInMatch extends ValidationError.Proto {
readonly _tag: "NoBuiltInMatch"
readonly error: HelpDoc
}

/**
Expand All @@ -111,6 +120,7 @@ export interface NoBuiltInMatch extends ValidationError.Proto {
*/
export interface UnclusteredFlag extends ValidationError.Proto {
readonly _tag: "UnclusteredFlag"
readonly error: HelpDoc
readonly unclustered: ReadonlyArray<string>
readonly rest: ReadonlyArray<string>
}
Expand All @@ -125,7 +135,6 @@ export declare namespace ValidationError {
*/
export interface Proto {
readonly [ValidationErrorTypeId]: ValidationErrorTypeId
readonly error: HelpDoc
}
}

Expand Down Expand Up @@ -168,8 +177,8 @@ export const isInvalidValue: (self: ValidationError) => self is InvalidValue =
* @since 1.0.0
* @category refinements
*/
export const isKeyValuesDetected: (self: ValidationError) => self is KeyValuesDetected =
InternalValidationError.isKeyValuesDetected
export const isMultipleValuesDetected: (self: ValidationError) => self is MultipleValuesDetected =
InternalValidationError.isMultipleValuesDetected

/**
* @since 1.0.0
Expand Down Expand Up @@ -241,7 +250,7 @@ export const invalidValue: (error: HelpDoc) => ValidationError =
export const keyValuesDetected: (
error: HelpDoc,
keyValues: ReadonlyArray<string>
) => ValidationError = InternalValidationError.keyValuesDetected
) => ValidationError = InternalValidationError.multipleValuesDetected

/**
* @since 1.0.0
Expand Down
25 changes: 5 additions & 20 deletions src/internal/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,21 +307,6 @@ export const optional = <A>(self: Args.Args<A>): Args.Args<Option.Option<A>> =>
export const repeated = <A>(self: Args.Args<A>): Args.Args<ReadonlyArray<A>> =>
makeVariadic(self, Option.none(), Option.none())

/** @internal */
export const repeatedAtLeastOnce = <A>(
self: Args.Args<A>
): Args.Args<ReadonlyArray.NonEmptyReadonlyArray<A>> =>
map(makeVariadic(self, Option.some(1), Option.none()), (values) => {
if (ReadonlyArray.isNonEmptyReadonlyArray(values)) {
return values
}
const message = Option.match(getIdentifierInternal(self as Instruction), {
onNone: () => "An anonymous variadic argument",
onSome: (identifier) => `The variadic option '${identifier}' `
})
throw new Error(`${message} is not respecting the required minimum of 1`)
})

/** @internal */
export const toRegularLanguage = <A>(
self: Args.Args<A>
Expand Down Expand Up @@ -507,9 +492,9 @@ const getMaxSizeInternal = (self: Instruction): number => {
return getMaxSizeInternal(self.args as Instruction)
}
case "Both": {
const leftMinSize = getMaxSizeInternal(self.left as Instruction)
const rightMinSize = getMaxSizeInternal(self.right as Instruction)
return leftMinSize + rightMinSize
const leftMaxSize = getMaxSizeInternal(self.left as Instruction)
const rightMaxSize = getMaxSizeInternal(self.right as Instruction)
return leftMaxSize + rightMaxSize
}
case "Variadic": {
const argsMaxSize = getMaxSizeInternal(self.args as Instruction)
Expand Down Expand Up @@ -725,11 +710,11 @@ const validateInternal = (
acc.length >= min1 && ReadonlyArray.isEmptyReadonlyArray(args)
? Effect.succeed([args, acc])
: Effect.fail(failure),
onSuccess: ([args, a]) => loop(args, ReadonlyArray.prepend(acc, a))
onSuccess: ([args, a]) => loop(args, ReadonlyArray.append(acc, a))
}))
}
return loop(args, ReadonlyArray.empty()).pipe(
Effect.map(([args, acc]) => [args, ReadonlyArray.reverse(acc)])
Effect.map(([args, acc]) => [args, acc])
)
}
}
Expand Down
Loading

0 comments on commit 714fe74

Please sign in to comment.