Skip to content

Commit

Permalink
Update to new record builder syntax (#2)
Browse files Browse the repository at this point in the history
* Update to new record builder syntax

* Fix documentation
  • Loading branch information
smores56 authored Jul 11, 2024
1 parent 6427f74 commit ff17f00
Show file tree
Hide file tree
Showing 12 changed files with 633 additions and 673 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ main =
Task.err (Exit 1 "")
cliParser =
Cli.weave {
alpha: <- Opt.u64 { short: "a", help: "Set the alpha level." },
force: <- Opt.flag { short: "f", help: "Force the task to complete." },
file: <- Param.maybeStr { name: "file", help: "The file to process." },
files: <- Param.strList { name: "files", help: "The rest of the files." },
{ Cli.weave <-
alpha: Opt.u64 { short: "a", help: "Set the alpha level." },
force: Opt.flag { short: "f", help: "Force the task to complete." },
file: Param.maybeStr { name: "file", help: "The file to process." },
files: Param.strList { name: "files", help: "The rest of the files." },
}
|> Cli.finish {
name: "basic",
Expand Down
10 changes: 5 additions & 5 deletions examples/basic.roc
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ main =
Task.err (Exit 1 "")

cliParser =
Cli.weave {
alpha: <- Opt.u64 { short: "a", help: "Set the alpha level." },
force: <- Opt.flag { short: "f", help: "Force the task to complete." },
file: <- Param.maybeStr { name: "file", help: "The file to process." },
files: <- Param.strList { name: "files", help: "The rest of the files." },
{ Cli.weave <-
alpha: Opt.u64 { short: "a", help: "Set the alpha level." },
force: Opt.flag { short: "f", help: "Force the task to complete." },
file: Param.maybeStr { name: "file", help: "The file to process." },
files: Param.strList { name: "files", help: "The rest of the files." },
}
|> Cli.finish {
name: "basic",
Expand Down
38 changes: 38 additions & 0 deletions examples/default-values.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
weaver: "../package/main.roc",
}

import pf.Stdout
import pf.Arg
import pf.Task exposing [Task]
import weaver.Opt
import weaver.Cli
import weaver.Param

main =
args = Arg.list!

when Cli.parseOrDisplayMessage cliParser args is
Ok data ->
Stdout.line! "Successfully parsed! Here's what I got:"
Stdout.line! ""
Stdout.line! (Inspect.toStr data)

Err message ->
Stdout.line! message

Task.err (Exit 1 "")

cliParser =
{ Cli.weave <-
alpha: Opt.maybeU64 { short: "a", long: "alpha", help: "Set the alpha level. [default: 123]" }
|> Cli.map \a -> Result.withDefault a 123,,
file: Param.maybeStr { name: "file", help: "The file to process. [default: NONE]" }
|> Cli.map \f -> Result.withDefault f "NONE",
}
|> Cli.finish {
name: "default-values",
version: "v0.0.1",
}
|> Cli.assertValid
33 changes: 33 additions & 0 deletions examples/single-arg.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
weaver: "../package/main.roc",
}

import pf.Stdout
import pf.Arg
import pf.Task exposing [Task]
import weaver.Opt
import weaver.Cli

main =
args = Arg.list!

when Cli.parseOrDisplayMessage cliParser args is
Ok data ->
Stdout.line! "Successfully parsed! Here's what I got:"
Stdout.line! ""
Stdout.line! (Inspect.toStr data)

Err message ->
Stdout.line! message

Task.err (Exit 1 "")

cliParser =
Opt.u64 { short: "a", long: "alpha", help: "Set the alpha level." }
|> Cli.map Alpha
|> Cli.finish {
name: "single-arg",
version: "v0.0.1",
}
|> Cli.assertValid
37 changes: 18 additions & 19 deletions examples/subcommands.roc
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ main =
Task.err (Exit 1 "")

cliParser =
Cli.weave {
force: <- Opt.flag { short: "f", help: "Force the task to complete." },
sc: <- Subcommand.optional [subcommandParser1, subcommandParser2],
file: <- Param.maybeStr { name: "file", help: "The file to process." },
files: <- Param.strList { name: "files", help: "The rest of the files." },
{ Cli.weave <-
force: Opt.flag { short: "f", help: "Force the task to complete." },
sc: Subcommand.optional [subcommandParser1, subcommandParser2],
file: Param.maybeStr { name: "file", help: "The file to process." },
files: Param.strList { name: "files", help: "The rest of the files." },
}
|> Cli.finish {
name: "subcommands",
Expand All @@ -41,34 +41,33 @@ cliParser =
|> Cli.assertValid

subcommandParser1 =
Cli.weave {
d: <- Opt.maybeU64 { short: "d", help: "A non-overlapping subcommand flag with s2." },
volume: <- Opt.maybeU64 { short: "v", long: "volume", help: "How loud to grind the gears." },
sc: <- Subcommand.optional [subSubcommandParser1, subSubcommandParser2],
{ Cli.weave <-
d: Opt.maybeU64 { short: "d", help: "A non-overlapping subcommand flag with s2." },
volume: Opt.maybeU64 { short: "v", long: "volume", help: "How loud to grind the gears." },
sc: Subcommand.optional [subSubcommandParser1, subSubcommandParser2],
}
|> Subcommand.finish { name: "s1", description: "A first subcommand.", mapper: S1 }

subcommandParser2 =
Cli.weave {
d: <- Opt.maybeU64 { short: "d", help: "This doesn't overlap with s1's -d flag." },
}
Opt.maybeU64 { short: "d", help: "This doesn't overlap with s1's -d flag." }
|> Cli.map DFlag
|> Subcommand.finish {
name: "s2",
description: "Another subcommand.",
mapper: S2,
}

subSubcommandParser1 =
Cli.weave {
a: <- Opt.u64 { short: "a", help: "An example short flag for a sub-subcommand." },
b: <- Opt.u64 { short: "b", help: "Another example short flag for a sub-subcommand." },
{ Cli.weave <-
a: Opt.u64 { short: "a", help: "An example short flag for a sub-subcommand." },
b: Opt.u64 { short: "b", help: "Another example short flag for a sub-subcommand." },
}
|> Subcommand.finish { name: "ss1", description: "A sub-subcommand.", mapper: SS1 }

subSubcommandParser2 =
Cli.weave {
a: <- Opt.u64 { short: "a", help: "Set the alpha level." },
c: <- Opt.u64 { short: "c", long: "create", help: "Create a doohickey." },
data: <- Param.str { name: "data", help: "Data to manipulate." },
{ Cli.weave <-
a: Opt.u64 { short: "a", help: "Set the alpha level." },
c: Opt.u64 { short: "c", long: "create", help: "Create a doohickey." },
data: Param.str { name: "data", help: "Data to manipulate." },
}
|> Subcommand.finish { name: "ss2", description: "Another sub-subcommand.", mapper: SS2 }
100 changes: 77 additions & 23 deletions package/Builder.roc
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ module [
GetParamsAction,
StopCollectingAction,
CliBuilder,
fromState,
addOptions,
addParameters,
fromArgParser,
fromFullParser,
addOption,
addParameter,
addSubcommands,
updateParser,
bindParser,
map,
combine,
intoParts,
checkForHelpAndVersion,
]
Expand All @@ -18,6 +21,7 @@ import Base exposing [
ArgParserState,
ArgParserResult,
onSuccessfulArgParse,
mapSuccessfullyParsed,
ArgExtractErr,
OptionConfig,
helpOption,
Expand All @@ -31,35 +35,49 @@ GetOptionsAction : { getOptions : {} }
GetParamsAction : { getParams : {} }
StopCollectingAction : []

CliBuilder state action := {
parser : ArgParser state,
CliBuilder data fromAction toAction := {
parser : ArgParser data,
options : List OptionConfig,
parameters : List ParameterConfig,
subcommands : Dict Str SubcommandConfig,
}

fromState : base -> CliBuilder base GetOptionsAction
fromState = \base ->
fromArgParser : (List Arg -> Result { data : data, remainingArgs : List Arg } ArgExtractErr) -> CliBuilder data fromAction toAction
fromArgParser = \parser ->
newParser = \{ args, subcommandPath } ->
when parser args is
Ok { data, remainingArgs } -> SuccessfullyParsed { data, remainingArgs, subcommandPath }
Err err -> IncorrectUsage err { subcommandPath }

@CliBuilder {
parser: newParser,
options: [],
parameters: [],
subcommands: Dict.empty {},
}

fromFullParser : ArgParser data -> CliBuilder data fromAction toAction
fromFullParser = \parser ->
@CliBuilder {
parser: \{ args, subcommandPath } -> SuccessfullyParsed { data: base, remainingArgs: args, subcommandPath },
parser,
options: [],
parameters: [],
subcommands: Dict.empty {},
}

addOptions : CliBuilder state action, List OptionConfig -> CliBuilder state action
addOptions = \@CliBuilder builder, newOptions ->
@CliBuilder { builder & options: List.concat builder.options newOptions }
addOption : CliBuilder state fromAction toAction, OptionConfig -> CliBuilder state fromAction toAction
addOption = \@CliBuilder builder, newOption ->
@CliBuilder { builder & options: List.append builder.options newOption }

addParameters : CliBuilder state action, List ParameterConfig -> CliBuilder state action
addParameters = \@CliBuilder builder, newParameters ->
@CliBuilder { builder & parameters: List.concat builder.parameters newParameters }
addParameter : CliBuilder state fromAction toAction, ParameterConfig -> CliBuilder state fromAction toAction
addParameter = \@CliBuilder builder, newParameter ->
@CliBuilder { builder & parameters: List.append builder.parameters newParameter }

addSubcommands : CliBuilder state action, Dict Str SubcommandConfig -> CliBuilder state action
addSubcommands : CliBuilder state fromAction toAction, Dict Str SubcommandConfig -> CliBuilder state fromAction toAction
addSubcommands = \@CliBuilder builder, newSubcommands ->
@CliBuilder { builder & subcommands: Dict.insertAll builder.subcommands newSubcommands }

setParser : CliBuilder state action, ArgParser nextState -> CliBuilder nextState nextAction
setParser : CliBuilder state fromAction toAction, ArgParser nextState -> CliBuilder nextState fromAction toAction
setParser = \@CliBuilder builder, parser ->
@CliBuilder {
options: builder.options,
Expand All @@ -68,7 +86,7 @@ setParser = \@CliBuilder builder, parser ->
parser,
}

updateParser : CliBuilder state action, ({ data : state, remainingArgs : List Arg } -> Result { data : nextState, remainingArgs : List Arg } ArgExtractErr) -> CliBuilder nextState nextAction
updateParser : CliBuilder state fromAction toAction, ({ data : state, remainingArgs : List Arg } -> Result { data : nextState, remainingArgs : List Arg } ArgExtractErr) -> CliBuilder nextState fromAction toAction
updateParser = \@CliBuilder builder, updater ->
newParser =
{ data, remainingArgs, subcommandPath } <- onSuccessfulArgParse builder.parser
Expand All @@ -79,7 +97,7 @@ updateParser = \@CliBuilder builder, updater ->

setParser (@CliBuilder builder) newParser

bindParser : CliBuilder state action, (ArgParserState state -> ArgParserResult (ArgParserState nextState)) -> CliBuilder nextState nextAction
bindParser : CliBuilder state fromAction toAction, (ArgParserState state -> ArgParserResult (ArgParserState nextState)) -> CliBuilder nextState fromAction toAction
bindParser = \@CliBuilder builder, updater ->
newParser : ArgParser nextState
newParser =
Expand All @@ -89,7 +107,7 @@ bindParser = \@CliBuilder builder, updater ->
setParser (@CliBuilder builder) newParser

intoParts :
CliBuilder state action
CliBuilder state fromAction toAction
-> {
parser : ArgParser state,
options : List OptionConfig,
Expand All @@ -98,6 +116,42 @@ intoParts :
}
intoParts = \@CliBuilder builder -> builder

map : CliBuilder a fromAction toAction, (a -> b) -> CliBuilder b fromAction toAction
map = \@CliBuilder builder, mapper ->
combinedParser = \input ->
builder.parser input
|> mapSuccessfullyParsed \{ data, remainingArgs, subcommandPath } ->
{ data: mapper data, remainingArgs, subcommandPath }

@CliBuilder {
parser: combinedParser,
options: builder.options,
parameters: builder.parameters,
subcommands: builder.subcommands,
}

combine : CliBuilder a action1 action2, CliBuilder b action2 action3, (a, b -> c) -> CliBuilder c action1 action3
combine = \@CliBuilder left, @CliBuilder right, combiner ->
combinedParser = \input ->
when left.parser input is
ShowVersion -> ShowVersion
ShowHelp sp -> ShowHelp sp
IncorrectUsage argExtractErr sp -> IncorrectUsage argExtractErr sp
SuccessfullyParsed { data, remainingArgs, subcommandPath } ->
when right.parser { args: remainingArgs, subcommandPath } is
ShowVersion -> ShowVersion
ShowHelp sp -> ShowHelp sp
IncorrectUsage argExtractErr sp -> IncorrectUsage argExtractErr sp
SuccessfullyParsed { data: data2, remainingArgs: restOfArgs, subcommandPath: nextSp } ->
SuccessfullyParsed { data: combiner data data2, remainingArgs: restOfArgs, subcommandPath: nextSp }

@CliBuilder {
parser: combinedParser,
options: List.concat left.options right.options,
parameters: List.concat left.parameters right.parameters,
subcommands: Dict.insertAll left.subcommands right.subcommands,
}

flagWasPassed : OptionConfig, List Arg -> Bool
flagWasPassed = \option, args ->
List.any args \arg ->
Expand All @@ -107,7 +161,7 @@ flagWasPassed = \option, args ->
Long long -> long.name == option.long
Parameter _p -> Bool.false

checkForHelpAndVersion : CliBuilder state action -> CliBuilder state action
checkForHelpAndVersion : CliBuilder state fromAction toAction -> CliBuilder state fromAction toAction
checkForHelpAndVersion = \@CliBuilder builder ->
newParser = \{ args, subcommandPath } ->
when builder.parser { args, subcommandPath } is
Expand All @@ -130,15 +184,15 @@ checkForHelpAndVersion = \@CliBuilder builder ->

expect
{ parser } =
fromState (\x -> { x })
|> updateParser \{ data, remainingArgs } -> Ok { data: data (Inspect.toStr remainingArgs), remainingArgs: [] }
fromArgParser \args -> Ok { data: Inspect.toStr args, remainingArgs: [] }
|> map Inspected
|> intoParts

out = parser { args: [Parameter "123"], subcommandPath: [] }

out
== SuccessfullyParsed {
data: { x: "[(Parameter \"123\")]" },
data: Inspected "[(Parameter \"123\")]",
remainingArgs: [],
subcommandPath: [],
}
Expand Down
Loading

0 comments on commit ff17f00

Please sign in to comment.