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

make Command, Option, Args and Primitive pipeable #316

Merged
merged 1 commit into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/beige-mice-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/cli": patch
---

Made `Command`, `Option`, `Args` and `Primitive` pipeable
3 changes: 2 additions & 1 deletion src/Args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { ValidationError } from "@effect/cli/ValidationError"
import type { Chunk, NonEmptyChunk } from "@effect/data/Chunk"
import type { Either } from "@effect/data/Either"
import type { Option } from "@effect/data/Option"
import type { Pipeable } from "@effect/data/Pipeable"
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray"
import type { Effect } from "@effect/io/Effect"

Expand All @@ -29,7 +30,7 @@ export type ArgsTypeId = typeof ArgsTypeId
* @since 1.0.0
* @category models
*/
export interface Args<A> extends Args.Variance<A> {}
export interface Args<A> extends Args.Variance<A>, Pipeable {}

/**
* @since 1.0.0
Expand Down
3 changes: 2 additions & 1 deletion src/Command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { Either } from "@effect/data/Either"
import type { HashMap } from "@effect/data/HashMap"
import type { HashSet } from "@effect/data/HashSet"
import type { Option } from "@effect/data/Option"
import type { Pipeable } from "@effect/data/Pipeable"
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray"
import type { Effect } from "@effect/io/Effect"

Expand All @@ -38,7 +39,7 @@ export type CommandTypeId = typeof CommandTypeId
* @since 1.0.0
* @category models
*/
export interface Command<A> extends Command.Variance<A> {}
export interface Command<A> extends Command.Variance<A>, Pipeable {}

/**
* @since 1.0.0
Expand Down
3 changes: 2 additions & 1 deletion src/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { Chunk, NonEmptyChunk } from "@effect/data/Chunk"
import type { Either } from "@effect/data/Either"
import type { HashMap } from "@effect/data/HashMap"
import type { Option } from "@effect/data/Option"
import type { Pipeable } from "@effect/data/Pipeable"
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray"
import type { Effect } from "@effect/io/Effect"

Expand All @@ -29,7 +30,7 @@ export type OptionsTypeId = typeof OptionsTypeId
* @since 1.0.0
* @category models
*/
export interface Options<A> extends Options.Variance<A> {}
export interface Options<A> extends Options.Variance<A>, Pipeable {}

/**
* @since 1.0.0
Expand Down
3 changes: 2 additions & 1 deletion src/Primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import type { Span } from "@effect/cli/HelpDoc/Span"
import * as internal from "@effect/cli/internal/primitive"
import type { Option } from "@effect/data/Option"
import type { Pipeable } from "@effect/data/Pipeable"
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray"
import type { Effect } from "@effect/io/Effect"

Expand Down Expand Up @@ -37,7 +38,7 @@ export declare namespace Primitive {
* @since 1.0.0
* @category models
*/
export interface Variance<A> {
export interface Variance<A> extends Pipeable {
readonly [PrimitiveTypeId]: {
readonly _A: (_: never) => A
}
Expand Down
4 changes: 4 additions & 0 deletions src/internal/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as Chunk from "@effect/data/Chunk"
import * as Either from "@effect/data/Either"
import { dual } from "@effect/data/Function"
import * as Option from "@effect/data/Option"
import { pipeArguments } from "@effect/data/Pipeable"
import * as ReadonlyArray from "@effect/data/ReadonlyArray"
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray"
import * as Effect from "@effect/io/Effect"
Expand All @@ -31,6 +32,9 @@ export type Op<Tag extends string, Body = {}> = Args.Args<never> & Body & {
const proto = {
[ArgsTypeId]: {
_A: (_: never) => _
},
pipe() {
return pipeArguments(this, arguments)
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/internal/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { dual, pipe } from "@effect/data/Function"
import * as HashMap from "@effect/data/HashMap"
import * as HashSet from "@effect/data/HashSet"
import * as Option from "@effect/data/Option"
import { pipeArguments } from "@effect/data/Pipeable"
import * as ReadonlyArray from "@effect/data/ReadonlyArray"
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray"
import * as Effect from "@effect/io/Effect"
Expand All @@ -41,6 +42,9 @@ const proto = {
[CommandTypeId]: {
_ArgsType: (_: never) => _,
_OptionsType: (_: never) => _
},
pipe() {
return pipeArguments(this, arguments)
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/internal/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { dual, pipe } from "@effect/data/Function"
import * as HashMap from "@effect/data/HashMap"
import * as Option from "@effect/data/Option"
import * as Order from "@effect/data/Order"
import { pipeArguments } from "@effect/data/Pipeable"
import type { Predicate } from "@effect/data/Predicate"
import * as RA from "@effect/data/ReadonlyArray"
import * as Effect from "@effect/io/Effect"
Expand All @@ -35,6 +36,9 @@ export type Op<Tag extends string, Body = {}> = Options.Options<never> & Body &
const proto = {
[OptionsTypeId]: {
_A: (_: never) => _
},
pipe() {
return pipeArguments(this, arguments)
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/internal/primitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as span from "@effect/cli/internal/helpDoc/span"
import type * as Primitive from "@effect/cli/Primitive"
import { dual, pipe } from "@effect/data/Function"
import * as Option from "@effect/data/Option"
import { pipeArguments } from "@effect/data/Pipeable"
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray"
import * as Effect from "@effect/io/Effect"

Expand All @@ -16,6 +17,9 @@ export const PrimitiveTypeId: Primitive.PrimitiveTypeId = Symbol.for(
const proto = {
[PrimitiveTypeId]: {
_A: (_: never) => _
},
pipe() {
return pipeArguments(this, arguments)
}
}

Expand Down
19 changes: 7 additions & 12 deletions test/Command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe.concurrent("Command", () => {
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const args = ["log"]
const command = pipe(Command.make("remote"), Command.orElse(Command.make("log")))
const command = Command.make("remote").pipe(Command.orElse(Command.make("log")))
const result = yield* $(Command.parse(command, args, config))
const expected = { name: "log", options: void 0, args: void 0 }
expect(result).toEqual(CommandDirective.userDefined([], expected))
Expand Down Expand Up @@ -97,8 +97,7 @@ describe.concurrent("Command", () => {
}))

describe.concurrent("Subcommands - no options or arguments", () => {
const git = pipe(
Command.make("git", { options: Options.alias(Options.boolean("verbose"), "v") }),
const git = Command.make("git", { options: Options.boolean("verbose").pipe(Options.alias("v")) }).pipe(
Command.subcommands([Command.make("remote"), Command.make("log")])
)

Expand Down Expand Up @@ -155,13 +154,11 @@ describe.concurrent("Command", () => {
})

describe.concurrent("Subcommands - with options and arguments", () => {
const rebaseOptions = pipe(
Options.boolean("i"),
Options.zip(Options.withDefault(Options.text("empty"), "drop"))
const rebaseOptions = Options.boolean("i").pipe(
Options.zip(Options.text("empty").pipe(Options.withDefault("drop")))
)
const rebaseArgs = Args.zip(Args.text(), Args.text())
const git = pipe(
Command.make("git"),
const git = Command.make("git").pipe(
Command.subcommands([
Command.make("rebase", { options: rebaseOptions, args: rebaseArgs })
])
Expand Down Expand Up @@ -212,11 +209,9 @@ describe.concurrent("Command", () => {
})

describe.concurrent("Subcommands - nested", () => {
const command = pipe(
Command.make("command"),
const command = Command.make("command").pipe(
Command.subcommands([
pipe(
Command.make("sub"),
Command.make("sub").pipe(
Command.subcommands([Command.make("subsub", { options: Options.boolean("i"), args: Args.text() })])
)
])
Expand Down
27 changes: 13 additions & 14 deletions test/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,23 @@ describe.concurrent("Options", () => {
it.effect("validates a text option", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.alias(Options.text("firstName"), "f")
const option = Options.text("firstName").pipe(Options.alias("f"))
const result = yield* $(Options.validate(option, ["--firstName", "John"], config))
expect(result).toEqual([[], "John"])
}))

it.effect("validates a text option with an alternative format", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.alias(Options.text("firstName"), "f")
const option = Options.text("firstName").pipe(Options.alias("f"))
const result = yield* $(Options.validate(option, ["--firstName=John"], config))
expect(result).toEqual([[], "John"])
}))

it.effect("validates a text option with an alias", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.alias(Options.text("firstName"), "f")
const option = Options.text("firstName").pipe(Options.alias("f"))
const result = yield* $(Options.validate(option, ["-f", "John"], config))
expect(result).toEqual([[], "John"])
}))
Expand All @@ -100,7 +100,7 @@ describe.concurrent("Options", () => {
it.effect("validates an option and returns the remainder", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.alias(Options.text("firstName"), "f")
const option = Options.text("firstName").pipe(Options.alias("f"))
const args = ["--firstName", "John", "--lastName", "Doe"]
const result = yield* $(Options.validate(option, args, config))
expect(result).toEqual([["--lastName", "Doe"], "John"])
Expand All @@ -109,7 +109,7 @@ describe.concurrent("Options", () => {
it.effect("validates an option and returns the remainder with different ordering", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.alias(Options.text("firstName"), "f")
const option = Options.text("firstName").pipe(Options.alias("f"))
const args = ["--bar", "baz", "--firstName", "John", "--lastName", "Doe"]
const result = yield* $(Options.validate(option, args, config))
expect(result).toEqual([["--bar", "baz", "--lastName", "Doe"], "John"])
Expand All @@ -118,7 +118,7 @@ describe.concurrent("Options", () => {
it.effect("does not validate when no valid values are passed", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.alias(Options.text("firstName"), "f")
const option = Options.text("firstName").pipe(Options.alias("f"))
const args = ["--lastName", "Doe"]
const result = yield* $(Effect.either(Options.validate(option, args, config)))
expect(result).toEqual(Either.left(ValidationError.missingValue(HelpDoc.p(Span.error(
Expand All @@ -129,7 +129,7 @@ describe.concurrent("Options", () => {
it.effect("does not validate when an option is passed without a corresponding value", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.alias(Options.text("firstName"), "f")
const option = Options.text("firstName").pipe(Options.alias("f"))
const args = ["--firstName"]
const result = yield* $(Effect.either(Options.validate(option, args, config)))
expect(result).toEqual(Either.left(ValidationError.invalidValue(HelpDoc.p(
Expand Down Expand Up @@ -184,15 +184,15 @@ describe.concurrent("Options", () => {
it.effect("validates an unsupplied optional option", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.optional(Options.integer("age"))
const option = Options.integer("age").pipe(Options.optional)
const result = yield* $(Options.validate(option, [], config))
expect(result).toEqual([[], Option.none()])
}))

it.effect("validates an unsupplied optional option with remainder", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.optional(Options.integer("age"))
const option = Options.integer("age").pipe(Options.optional)
const args = ["--bar", "baz"]
const result = yield* $(Options.validate(option, args, config))
expect(result).toEqual([args, Option.none()])
Expand All @@ -201,7 +201,7 @@ describe.concurrent("Options", () => {
it.effect("validates a supplied optional option", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.optional(Options.integer("age"))
const option = Options.integer("age").pipe(Options.optional)
const args = ["--age", "20"]
const result = yield* $(Options.validate(option, args, config))
expect(result).toEqual([[], Option.some(20)])
Expand All @@ -210,7 +210,7 @@ describe.concurrent("Options", () => {
it.effect("validates a supplied optional option with remainder", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.optional(Options.integer("age"))
const option = Options.integer("age").pipe(Options.optional)
const args = ["--firstName", "John", "--age", "20", "--lastName", "Doe"]
const result = yield* $(Options.validate(option, args, config))
expect(result).toEqual([["--firstName", "John", "--lastName", "Doe"], Option.some(20)])
Expand Down Expand Up @@ -245,7 +245,7 @@ describe.concurrent("Options", () => {
it.effect("validate provides a suggestion if a provided option with a default is close to a specified option", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = Options.withDefault(Options.text("firstName"), "Jack")
const option = Options.text("firstName").pipe(Options.withDefault("Jack"))
const args = ["--firstme", "Alice"]
const result = yield* $(Effect.flip(Options.validate(option, args, config)))
expect(result).toEqual(ValidationError.invalidValue(HelpDoc.p(Span.error(
Expand Down Expand Up @@ -300,8 +300,7 @@ describe.concurrent("Options", () => {
it.effect("orElse - invalid option provided with a default", () =>
Effect.gen(function*($) {
const config = CliConfig.defaultConfig
const option = pipe(
Options.integer("min"),
const option = Options.integer("min").pipe(
Options.orElse(Options.integer("max")),
Options.withDefault(0)
)
Expand Down
Loading