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

Commit

Permalink
rename HandledCommand to Command (#392)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart authored Nov 28, 2023
1 parent 585b602 commit a6fe132
Show file tree
Hide file tree
Showing 14 changed files with 2,299 additions and 2,093 deletions.
136 changes: 39 additions & 97 deletions examples/minigit.ts
Original file line number Diff line number Diff line change
@@ -1,124 +1,66 @@
import * as Args from "@effect/cli/Args"
import * as CliApp from "@effect/cli/CliApp"
import * as Command from "@effect/cli/Command"
import * as Handled from "@effect/cli/Command"
import * as Options from "@effect/cli/Options"
import * as NodeContext from "@effect/platform-node/NodeContext"
import * as Runtime from "@effect/platform-node/Runtime"
import * as Console from "effect/Console"
import * as Data from "effect/Data"
import * as Effect from "effect/Effect"
import { pipe } from "effect/Function"
import type * as HashMap from "effect/HashMap"
import * as Option from "effect/Option"
import * as ReadonlyArray from "effect/ReadonlyArray"

// =============================================================================
// Models
// =============================================================================

type Subcommand = AddSubcommand | CloneSubcommand

interface AddSubcommand extends Data.Case {
readonly _tag: "AddSubcommand"
readonly verbose: boolean
}
const AddSubcommand = Data.tagged<AddSubcommand>("AddSubcommand")

interface CloneSubcommand extends Data.Case {
readonly _tag: "CloneSubcommand"
readonly depth: Option.Option<number>
readonly repository: string
readonly directory: Option.Option<string>
}
const CloneSubcommand = Data.tagged<CloneSubcommand>("CloneSubcommand")

// =============================================================================
// Commands
// =============================================================================

// minigit [--version] [-h | --help] [-c <name>=<value>]
const minigitOptions = Options.keyValueMap("c").pipe(Options.optional)
const minigit = Command.make("minigit", { options: minigitOptions })
const minigit = Handled.make(
"minigit",
{ configs: Options.keyValueMap("c").pipe(Options.optional) },
({ configs }) =>
Option.match(configs, {
onNone: () => Console.log("Running 'minigit'"),
onSome: (configs) => {
const keyValuePairs = Array.from(configs).map(([key, value]) => `${key}=${value}`).join(
", "
)
return Console.log(`Running 'minigit' with the following configs: ${keyValuePairs}`)
}
})
)

// minigit add [-v | --verbose] [--] [<pathspec>...]
const minigitAddOptions = Options.boolean("verbose").pipe(Options.withAlias("v"))
const minigitAdd = Command.make("add", { options: minigitAddOptions }).pipe(
Command.map((parsed) => AddSubcommand({ verbose: parsed.options }))
)
const minigitAdd = Handled.make("add", {
verbose: Options.boolean("verbose").pipe(Options.withAlias("v"))
}, ({ verbose }) => Console.log(`Running 'minigit add' with '--verbose ${verbose}'`))

// minigit clone [--depth <depth>] [--] <repository> [<directory>]
const minigitCloneArgs = Args.all([
Args.text({ name: "repository" }),
Args.directory().pipe(Args.optional)
])
const minigitCloneOptions = Options.integer("depth").pipe(Options.optional)
const minigitClone = Command.make("clone", {
options: minigitCloneOptions,
args: minigitCloneArgs
}).pipe(Command.map((parsed) =>
CloneSubcommand({
depth: parsed.options,
repository: parsed.args[0],
directory: parsed.args[1]
})
))
const minigitClone = Handled.make("clone", {
repository: Args.text({ name: "repository" }),
directory: Args.directory().pipe(Args.optional),
depth: Options.integer("depth").pipe(Options.optional)
}, ({ depth, directory, repository }) => {
const optionsAndArgs = pipe(
ReadonlyArray.compact([
Option.map(depth, (depth) => `--depth ${depth}`),
Option.some(repository),
directory
]),
ReadonlyArray.join(", ")
)
return Console.log(
`Running 'minigit clone' with the following options and arguments: '${optionsAndArgs}'`
)
})

const finalCommand = minigit.pipe(Command.withSubcommands([minigitAdd, minigitClone]))
const finalCommand = minigit.pipe(Handled.withSubcommands([minigitAdd, minigitClone]))

// =============================================================================
// Application
// =============================================================================

const cliApp = CliApp.make({
const run = Handled.run(finalCommand, {
name: "MiniGit Distributed Version Control",
version: "v2.42.1",
command: finalCommand
})

// =============================================================================
// Execution
// =============================================================================

const handleRootCommand = (configs: Option.Option<HashMap.HashMap<string, string>>) =>
Option.match(configs, {
onNone: () => Console.log("Running 'minigit'"),
onSome: (configs) => {
const keyValuePairs = Array.from(configs).map(([key, value]) => `${key}=${value}`).join(", ")
return Console.log(`Running 'minigit' with the following configs: ${keyValuePairs}`)
}
})

const handleSubcommand = (subcommand: Subcommand) => {
switch (subcommand._tag) {
case "AddSubcommand": {
return Console.log(`Running 'minigit add' with '--verbose ${subcommand.verbose}'`)
}
case "CloneSubcommand": {
const optionsAndArgs = pipe(
ReadonlyArray.compact([
Option.map(subcommand.depth, (depth) => `--depth ${depth}`),
Option.some(subcommand.repository),
subcommand.directory
]),
ReadonlyArray.join(", ")
)
return Console.log(
`Running 'minigit clone' with the following options and arguments: '${optionsAndArgs}'`
)
}
}
}

const program = Effect.gen(function*(_) {
const args = yield* _(Effect.sync(() => globalThis.process.argv.slice(2)))
return yield* _(CliApp.run(cliApp, args, (parsed) =>
Option.match(parsed.subcommand, {
onNone: () => handleRootCommand(parsed.options),
onSome: (subcommand) => handleSubcommand(subcommand)
})))
version: "v2.42.1"
})

program.pipe(
Effect.suspend(() => run(process.argv.slice(2))).pipe(
Effect.provide(NodeContext.layer),
Runtime.runMain
)
27 changes: 13 additions & 14 deletions examples/naval-fate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Args, Command, HandledCommand, Options } from "@effect/cli"
import { Args, Command, Options } from "@effect/cli"
import * as KeyValueStore from "@effect/platform-node/KeyValueStore"
import * as NodeContext from "@effect/platform-node/NodeContext"
import * as Runtime from "@effect/platform-node/Runtime"
Expand Down Expand Up @@ -32,11 +32,11 @@ const speedOption = Options.integer("speed").pipe(
Options.withDefault(10)
)

const shipCommandParent = HandledCommand.makeHelp("ship", {
const shipCommandParent = Command.makeHelp("ship", {
verbose: Options.boolean("verbose")
})

const newShipCommand = HandledCommand.make("new", {
const newShipCommand = Command.make("new", {
name: nameArg
}, ({ name }) =>
Effect.gen(function*(_) {
Expand All @@ -48,7 +48,7 @@ const newShipCommand = HandledCommand.make("new", {
}
}))

const moveShipCommand = HandledCommand.make("move", {
const moveShipCommand = Command.make("move", {
...nameAndCoordinatesArg,
speed: speedOption
}, ({ name, speed, x, y }) =>
Expand All @@ -57,7 +57,7 @@ const moveShipCommand = HandledCommand.make("move", {
yield* _(Console.log(`Moving ship '${name}' to coordinates (${x}, ${y}) at ${speed} knots`))
}))

const shootShipCommand = HandledCommand.make(
const shootShipCommand = Command.make(
"shoot",
{ ...coordinatesArg },
({ x, y }) =>
Expand All @@ -67,15 +67,15 @@ const shootShipCommand = HandledCommand.make(
})
)

const shipCommand = HandledCommand.withSubcommands(shipCommandParent, [
const shipCommand = Command.withSubcommands(shipCommandParent, [
newShipCommand,
moveShipCommand,
shootShipCommand
])

const mineCommandParent = HandledCommand.makeHelp("mine")
const mineCommandParent = Command.makeHelp("mine")

const setMineCommand = HandledCommand.make("set", {
const setMineCommand = Command.make("set", {
...coordinatesArg,
moored: mooredOption
}, ({ moored, x, y }) =>
Expand All @@ -86,24 +86,23 @@ const setMineCommand = HandledCommand.make("set", {
)
}))

const removeMineCommand = HandledCommand.make("remove", {
const removeMineCommand = Command.make("remove", {
...coordinatesArg
}, ({ x, y }) =>
Effect.gen(function*(_) {
yield* _(removeMine(x, y))
yield* _(Console.log(`Removing mine at coordinates (${x}, ${y}), if present`))
}))

const mineCommand = HandledCommand.withSubcommands(mineCommandParent, [
const mineCommand = Command.withSubcommands(mineCommandParent, [
setMineCommand,
removeMineCommand
])

const run = Command.make("naval_fate").pipe(
const run = Command.makeHelp("naval_fate").pipe(
Command.withDescription("An implementation of the Naval Fate CLI application."),
HandledCommand.fromCommandHelp,
HandledCommand.withSubcommands([shipCommand, mineCommand]),
HandledCommand.toAppAndRun({
Command.withSubcommands([shipCommand, mineCommand]),
Command.run({
name: "Naval Fate",
version: "1.0.0"
})
Expand Down
2 changes: 1 addition & 1 deletion examples/prompt.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as CliApp from "@effect/cli/CliApp"
import * as Command from "@effect/cli/Command"
import * as Command from "@effect/cli/CommandDescriptor"
import * as Prompt from "@effect/cli/Prompt"
import * as NodeContext from "@effect/platform-node/NodeContext"
import * as Runtime from "@effect/platform-node/Runtime"
Expand Down
2 changes: 1 addition & 1 deletion src/BuiltInOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import type { Option } from "effect/Option"
import type { Command } from "./Command.js"
import type { Command } from "./CommandDescriptor.js"
import type { HelpDoc } from "./HelpDoc.js"
import * as InternalBuiltInOptions from "./internal/builtInOptions.js"
import type { Options } from "./Options.js"
Expand Down
2 changes: 1 addition & 1 deletion src/CliApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Path } from "@effect/platform/Path"
import type { Terminal } from "@effect/platform/Terminal"
import type { Effect } from "effect/Effect"
import type { Pipeable } from "effect/Pipeable"
import type { Command } from "./Command.js"
import type { Command } from "./CommandDescriptor.js"
import type { HelpDoc } from "./HelpDoc.js"
import type { Span } from "./HelpDoc/Span.js"
import * as InternalCliApp from "./internal/cliApp.js"
Expand Down
Loading

0 comments on commit a6fe132

Please sign in to comment.