diff options
Diffstat (limited to 'aoc2023/build/packages/glint/src')
-rw-r--r-- | aoc2023/build/packages/glint/src/glint.app.src | 13 | ||||
-rw-r--r-- | aoc2023/build/packages/glint/src/glint.erl | 513 | ||||
-rw-r--r-- | aoc2023/build/packages/glint/src/glint.gleam | 588 | ||||
-rw-r--r-- | aoc2023/build/packages/glint/src/glint/flag.gleam | 478 | ||||
-rw-r--r-- | aoc2023/build/packages/glint/src/glint/flag/constraint.gleam | 66 | ||||
-rw-r--r-- | aoc2023/build/packages/glint/src/glint@flag.erl | 523 | ||||
-rw-r--r-- | aoc2023/build/packages/glint/src/glint@flag@constraint.erl | 68 |
7 files changed, 0 insertions, 2249 deletions
diff --git a/aoc2023/build/packages/glint/src/glint.app.src b/aoc2023/build/packages/glint/src/glint.app.src deleted file mode 100644 index 7eb7649..0000000 --- a/aoc2023/build/packages/glint/src/glint.app.src +++ /dev/null @@ -1,13 +0,0 @@ -{application, glint, [ - {vsn, "0.13.0"}, - {applications, [gleam_community_ansi, - gleam_community_colour, - gleam_stdlib, - gleeunit, - snag]}, - {description, "Gleam command line argument parsing with basic flag support."}, - {modules, [glint, - glint@flag, - glint@flag@constraint]}, - {registered, []} -]}. diff --git a/aoc2023/build/packages/glint/src/glint.erl b/aoc2023/build/packages/glint/src/glint.erl deleted file mode 100644 index 0501cc6..0000000 --- a/aoc2023/build/packages/glint/src/glint.erl +++ /dev/null @@ -1,513 +0,0 @@ --module(glint). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([with_config/2, with_pretty_help/2, without_pretty_help/1, with_name/2, new/0, command/1, description/2, flag/3, flag_tuple/2, flags/2, global_flag/3, global_flag_tuple/2, global_flags/2, default_pretty_help/0, add/3, help_flag/0, execute/2, run_and_handle/3, run/2, add_command_from_stub/2]). --export_type([config/0, pretty_help/0, glint/1, command/1, command_input/0, command_node/1, out/1, stub/1]). - --type config() :: {config, - gleam@option:option(pretty_help()), - gleam@option:option(binary())}. - --type pretty_help() :: {pretty_help, - gleam_community@colour:colour(), - gleam_community@colour:colour(), - gleam_community@colour:colour()}. - --opaque glint(GHR) :: {glint, - config(), - command_node(GHR), - gleam@map:map_(binary(), glint@flag:flag())}. - --opaque command(GHS) :: {command, - fun((command_input()) -> GHS), - gleam@map:map_(binary(), glint@flag:flag()), - binary()}. - --type command_input() :: {command_input, - list(binary()), - gleam@map:map_(binary(), glint@flag:flag())}. - --type command_node(GHT) :: {command_node, - gleam@option:option(command(GHT)), - gleam@map:map_(binary(), command_node(GHT))}. - --type out(GHU) :: {out, GHU} | {help, binary()}. - --type stub(GHV) :: {stub, - list(binary()), - fun((command_input()) -> GHV), - list({binary(), glint@flag:flag()}), - binary()}. - --spec with_config(glint(GIA), config()) -> glint(GIA). -with_config(Glint, Config) -> - erlang:setelement(2, Glint, Config). - --spec with_pretty_help(glint(GID), pretty_help()) -> glint(GID). -with_pretty_help(Glint, Pretty) -> - _pipe = erlang:setelement(2, erlang:element(2, Glint), {some, Pretty}), - with_config(Glint, _pipe). - --spec without_pretty_help(glint(GIG)) -> glint(GIG). -without_pretty_help(Glint) -> - _pipe = erlang:setelement(2, erlang:element(2, Glint), none), - with_config(Glint, _pipe). - --spec with_name(glint(GIJ), binary()) -> glint(GIJ). -with_name(Glint, Name) -> - _pipe = erlang:setelement(3, erlang:element(2, Glint), {some, Name}), - with_config(Glint, _pipe). - --spec empty_command() -> command_node(any()). -empty_command() -> - {command_node, none, gleam@map:new()}. - --spec new() -> glint(any()). -new() -> - {glint, {config, none, none}, empty_command(), gleam@map:new()}. - --spec do_add(command_node(GIT), list(binary()), command(GIT)) -> command_node(GIT). -do_add(Root, Path, Contents) -> - case Path of - [] -> - erlang:setelement(2, Root, {some, Contents}); - - [X | Xs] -> - erlang:setelement( - 3, - Root, - (gleam@map:update( - erlang:element(3, Root), - X, - fun(Node) -> _pipe = Node, - _pipe@1 = gleam@option:lazy_unwrap( - _pipe, - fun empty_command/0 - ), - do_add(_pipe@1, Xs, Contents) end - )) - ) - end. - --spec command(fun((command_input()) -> GJC)) -> command(GJC). -command(Runner) -> - {command, Runner, gleam@map:new(), <<""/utf8>>}. - --spec description(command(GJF), binary()) -> command(GJF). -description(Cmd, Description) -> - erlang:setelement(4, Cmd, Description). - --spec flag(command(GJI), binary(), glint@flag:flag_builder(any())) -> command(GJI). -flag(Cmd, Key, Flag) -> - erlang:setelement( - 3, - Cmd, - gleam@map:insert(erlang:element(3, Cmd), Key, glint@flag:build(Flag)) - ). - --spec flag_tuple(command(GJN), {binary(), glint@flag:flag_builder(any())}) -> command(GJN). -flag_tuple(Cmd, Tup) -> - flag(Cmd, erlang:element(1, Tup), erlang:element(2, Tup)). - --spec flags(command(GJS), list({binary(), glint@flag:flag()})) -> command(GJS). -flags(Cmd, Flags) -> - gleam@list:fold( - Flags, - Cmd, - fun(Cmd@1, _use1) -> - {Key, Flag} = _use1, - erlang:setelement( - 3, - Cmd@1, - gleam@map:insert(erlang:element(3, Cmd@1), Key, Flag) - ) - end - ). - --spec global_flag(glint(GJW), binary(), glint@flag:flag_builder(any())) -> glint(GJW). -global_flag(Glint, Key, Flag) -> - erlang:setelement( - 4, - Glint, - gleam@map:insert(erlang:element(4, Glint), Key, glint@flag:build(Flag)) - ). - --spec global_flag_tuple(glint(GKB), {binary(), glint@flag:flag_builder(any())}) -> glint(GKB). -global_flag_tuple(Glint, Tup) -> - global_flag(Glint, erlang:element(1, Tup), erlang:element(2, Tup)). - --spec global_flags(glint(GKG), list({binary(), glint@flag:flag()})) -> glint(GKG). -global_flags(Glint, Flags) -> - erlang:setelement( - 4, - Glint, - (gleam@list:fold( - Flags, - erlang:element(4, Glint), - fun(Acc, Tup) -> - gleam@map:insert( - Acc, - erlang:element(1, Tup), - erlang:element(2, Tup) - ) - end - )) - ). - --spec execute_root( - command_node(GKU), - gleam@map:map_(binary(), glint@flag:flag()), - list(binary()), - list(binary()) -) -> {ok, out(GKU)} | {error, snag:snag()}. -execute_root(Cmd, Global_flags, Args, Flag_inputs) -> - _pipe@3 = case erlang:element(2, Cmd) of - {some, Contents} -> - gleam@result:'try'( - gleam@list:try_fold( - Flag_inputs, - gleam@map:merge(Global_flags, erlang:element(3, Contents)), - fun glint@flag:update_flags/2 - ), - fun(New_flags) -> _pipe = {command_input, Args, New_flags}, - _pipe@1 = (erlang:element(2, Contents))(_pipe), - _pipe@2 = {out, _pipe@1}, - {ok, _pipe@2} end - ); - - none -> - snag:error(<<"command not found"/utf8>>) - end, - snag:context(_pipe@3, <<"failed to run command"/utf8>>). - --spec default_pretty_help() -> pretty_help(). -default_pretty_help() -> - _assert_subject = gleam_community@colour:from_rgb255(182, 255, 234), - {ok, Usage_colour} = case _assert_subject of - {ok, _} -> _assert_subject; - _assert_fail -> - erlang:error(#{gleam_error => let_assert, - message => <<"Assertion pattern match failed"/utf8>>, - value => _assert_fail, - module => <<"glint"/utf8>>, - function => <<"default_pretty_help"/utf8>>, - line => 404}) - end, - _assert_subject@1 = gleam_community@colour:from_rgb255(255, 175, 243), - {ok, Flags_colour} = case _assert_subject@1 of - {ok, _} -> _assert_subject@1; - _assert_fail@1 -> - erlang:error(#{gleam_error => let_assert, - message => <<"Assertion pattern match failed"/utf8>>, - value => _assert_fail@1, - module => <<"glint"/utf8>>, - function => <<"default_pretty_help"/utf8>>, - line => 405}) - end, - _assert_subject@2 = gleam_community@colour:from_rgb255(252, 226, 174), - {ok, Subcommands_colour} = case _assert_subject@2 of - {ok, _} -> _assert_subject@2; - _assert_fail@2 -> - erlang:error(#{gleam_error => let_assert, - message => <<"Assertion pattern match failed"/utf8>>, - value => _assert_fail@2, - module => <<"glint"/utf8>>, - function => <<"default_pretty_help"/utf8>>, - line => 406}) - end, - {pretty_help, Usage_colour, Flags_colour, Subcommands_colour}. - --spec is_not_empty(binary()) -> boolean(). -is_not_empty(S) -> - S /= <<""/utf8>>. - --spec sanitize_path(list(binary())) -> list(binary()). -sanitize_path(Path) -> - _pipe = Path, - _pipe@1 = gleam@list:map(_pipe, fun gleam@string:trim/1), - gleam@list:filter(_pipe@1, fun is_not_empty/1). - --spec add(glint(GIO), list(binary()), command(GIO)) -> glint(GIO). -add(Glint, Path, Contents) -> - erlang:setelement( - 3, - Glint, - begin - _pipe = Path, - _pipe@1 = sanitize_path(_pipe), - do_add(erlang:element(3, Glint), _pipe@1, Contents) - end - ). - --spec help_flag() -> binary(). -help_flag() -> - <<(<<"--"/utf8>>)/binary, "help"/utf8>>. - --spec wrap_with_space(binary()) -> binary(). -wrap_with_space(S) -> - case S of - <<""/utf8>> -> - <<" "/utf8>>; - - _ -> - <<<<" "/utf8, S/binary>>/binary, " "/utf8>> - end. - --spec subcommand_help(binary(), command_node(any())) -> binary(). -subcommand_help(Name, Cmd) -> - case erlang:element(2, Cmd) of - none -> - Name; - - {some, Contents} -> - <<<<Name/binary, "\t\t"/utf8>>/binary, - (erlang:element(4, Contents))/binary>> - end. - --spec subcommands_help(gleam@map:map_(binary(), command_node(any()))) -> binary(). -subcommands_help(Cmds) -> - _pipe = Cmds, - _pipe@1 = gleam@map:map_values(_pipe, fun subcommand_help/2), - _pipe@2 = gleam@map:values(_pipe@1), - _pipe@3 = gleam@list:sort(_pipe@2, fun gleam@string:compare/2), - gleam@string:join(_pipe@3, <<"\n\t"/utf8>>). - --spec heading_style(binary(), gleam_community@colour:colour()) -> binary(). -heading_style(Heading, Colour) -> - _pipe = Heading, - _pipe@1 = gleam_community@ansi:bold(_pipe), - _pipe@2 = gleam_community@ansi:underline(_pipe@1), - _pipe@3 = gleam_community@ansi:italic(_pipe@2), - _pipe@4 = gleam_community@ansi:hex( - _pipe@3, - gleam_community@colour:to_rgb_hex(Colour) - ), - gleam_community@ansi:reset(_pipe@4). - --spec usage_help( - binary(), - gleam@map:map_(binary(), glint@flag:flag()), - config() -) -> binary(). -usage_help(Cmd_name, Flags, Config) -> - App_name = gleam@option:unwrap( - erlang:element(3, Config), - <<"gleam run"/utf8>> - ), - Flags@1 = begin - _pipe = Flags, - _pipe@1 = gleam@map:to_list(_pipe), - _pipe@2 = gleam@list:map(_pipe@1, fun glint@flag:flag_type_help/1), - gleam@list:sort(_pipe@2, fun gleam@string:compare/2) - end, - Flag_sb = case Flags@1 of - [] -> - gleam@string_builder:new(); - - _ -> - _pipe@3 = Flags@1, - _pipe@4 = gleam@list:intersperse(_pipe@3, <<" "/utf8>>), - _pipe@5 = gleam@string_builder:from_strings(_pipe@4), - _pipe@6 = gleam@string_builder:prepend(_pipe@5, <<" [ "/utf8>>), - gleam@string_builder:append(_pipe@6, <<" ]"/utf8>>) - end, - _pipe@7 = [App_name, wrap_with_space(Cmd_name), <<"[ ARGS ]"/utf8>>], - _pipe@8 = gleam@string_builder:from_strings(_pipe@7), - _pipe@9 = gleam@string_builder:append_builder(_pipe@8, Flag_sb), - _pipe@12 = gleam@string_builder:prepend( - _pipe@9, - <<(begin - _pipe@10 = erlang:element(2, Config), - _pipe@11 = gleam@option:map( - _pipe@10, - fun(Styling) -> - heading_style( - <<"USAGE:"/utf8>>, - erlang:element(2, Styling) - ) - end - ), - gleam@option:unwrap(_pipe@11, <<"USAGE:"/utf8>>) - end)/binary, - "\n\t"/utf8>> - ), - gleam@string_builder:to_string(_pipe@12). - --spec cmd_help( - list(binary()), - command_node(any()), - config(), - gleam@map:map_(binary(), glint@flag:flag()) -) -> binary(). -cmd_help(Path, Cmd, Config, Global_flags) -> - Name = begin - _pipe = Path, - _pipe@1 = gleam@list:reverse(_pipe), - gleam@string:join(_pipe@1, <<" "/utf8>>) - end, - Flags = begin - _pipe@2 = gleam@option:map( - erlang:element(2, Cmd), - fun(Contents) -> erlang:element(3, Contents) end - ), - _pipe@3 = gleam@option:lazy_unwrap(_pipe@2, fun gleam@map:new/0), - gleam@map:merge(Global_flags, _pipe@3) - end, - Flags_help_body = <<<<(begin - _pipe@4 = erlang:element(2, Config), - _pipe@5 = gleam@option:map( - _pipe@4, - fun(P) -> - heading_style(<<"FLAGS:"/utf8>>, erlang:element(3, P)) - end - ), - gleam@option:unwrap(_pipe@5, <<"FLAGS:"/utf8>>) - end)/binary, - "\n\t"/utf8>>/binary, - (gleam@string:join( - gleam@list:sort( - [<<"--help\t\t\tPrint help information"/utf8>> | - glint@flag:flags_help(Flags)], - fun gleam@string:compare/2 - ), - <<"\n\t"/utf8>> - ))/binary>>, - Usage = usage_help(Name, Flags, Config), - Description = begin - _pipe@6 = erlang:element(2, Cmd), - _pipe@7 = gleam@option:map( - _pipe@6, - fun(Contents@1) -> erlang:element(4, Contents@1) end - ), - gleam@option:unwrap(_pipe@7, <<""/utf8>>) - end, - Header_items = begin - _pipe@8 = [Name, Description], - _pipe@9 = gleam@list:filter(_pipe@8, fun is_not_empty/1), - gleam@string:join(_pipe@9, <<"\n"/utf8>>) - end, - Subcommands = case subcommands_help(erlang:element(3, Cmd)) of - <<""/utf8>> -> - <<""/utf8>>; - - Subcommands_help_body -> - <<<<(begin - _pipe@10 = erlang:element(2, Config), - _pipe@11 = gleam@option:map( - _pipe@10, - fun(P@1) -> - heading_style( - <<"SUBCOMMANDS:"/utf8>>, - erlang:element(4, P@1) - ) - end - ), - gleam@option:unwrap(_pipe@11, <<"SUBCOMMANDS:"/utf8>>) - end)/binary, - "\n\t"/utf8>>/binary, - Subcommands_help_body/binary>> - end, - _pipe@12 = [Header_items, Usage, Flags_help_body, Subcommands], - _pipe@13 = gleam@list:filter(_pipe@12, fun is_not_empty/1), - gleam@string:join(_pipe@13, <<"\n\n"/utf8>>). - --spec do_execute( - command_node(GKO), - config(), - gleam@map:map_(binary(), glint@flag:flag()), - list(binary()), - list(binary()), - boolean(), - list(binary()) -) -> {ok, out(GKO)} | {error, snag:snag()}. -do_execute(Cmd, Config, Global_flags, Args, Flags, Help, Command_path) -> - case Args of - [] when Help -> - _pipe = Command_path, - _pipe@1 = cmd_help(_pipe, Cmd, Config, Global_flags), - _pipe@2 = {help, _pipe@1}, - {ok, _pipe@2}; - - [] -> - execute_root(Cmd, Global_flags, [], Flags); - - [Arg | Rest] -> - case gleam@map:get(erlang:element(3, Cmd), Arg) of - {ok, Cmd@1} -> - do_execute( - Cmd@1, - Config, - Global_flags, - Rest, - Flags, - Help, - [Arg | Command_path] - ); - - _ when Help -> - _pipe@3 = Command_path, - _pipe@4 = cmd_help(_pipe@3, Cmd, Config, Global_flags), - _pipe@5 = {help, _pipe@4}, - {ok, _pipe@5}; - - _ -> - execute_root(Cmd, Global_flags, Args, Flags) - end - end. - --spec execute(glint(GKK), list(binary())) -> {ok, out(GKK)} | - {error, snag:snag()}. -execute(Glint, Args) -> - Help_flag = help_flag(), - {Help, Args@2} = case gleam@list:pop(Args, fun(S) -> S =:= Help_flag end) of - {ok, {_, Args@1}} -> - {true, Args@1}; - - _ -> - {false, Args} - end, - {Flags, Args@3} = gleam@list:partition( - Args@2, - fun(_capture) -> gleam@string:starts_with(_capture, <<"--"/utf8>>) end - ), - do_execute( - erlang:element(3, Glint), - erlang:element(2, Glint), - erlang:element(4, Glint), - Args@3, - Flags, - Help, - [] - ). - --spec run_and_handle(glint(GLC), list(binary()), fun((GLC) -> any())) -> nil. -run_and_handle(Glint, Args, Handle) -> - case execute(Glint, Args) of - {error, Err} -> - _pipe = Err, - _pipe@1 = snag:pretty_print(_pipe), - gleam@io:println(_pipe@1); - - {ok, {help, Help}} -> - gleam@io:println(Help); - - {ok, {out, Out}} -> - Handle(Out), - nil - end. - --spec run(glint(any()), list(binary())) -> nil. -run(Glint, Args) -> - run_and_handle(Glint, Args, gleam@function:constant(nil)). - --spec add_command_from_stub(glint(GLP), stub(GLP)) -> glint(GLP). -add_command_from_stub(Glint, Stub) -> - add( - Glint, - erlang:element(2, Stub), - begin - _pipe = command(erlang:element(3, Stub)), - _pipe@1 = flags(_pipe, erlang:element(4, Stub)), - description(_pipe@1, erlang:element(5, Stub)) - end - ). diff --git a/aoc2023/build/packages/glint/src/glint.gleam b/aoc2023/build/packages/glint/src/glint.gleam deleted file mode 100644 index b159016..0000000 --- a/aoc2023/build/packages/glint/src/glint.gleam +++ /dev/null @@ -1,588 +0,0 @@ -import gleam/map.{type Map} -import gleam/option.{type Option, None, Some} -import gleam/list -import gleam/io -import gleam/string -import snag.{type Result} -import glint/flag.{type Flag, type Map as FlagMap} -import gleam/string_builder as sb -import gleam_community/ansi -import gleam_community/colour.{type Colour} -import gleam/result -import gleam/function - -// --- CONFIGURATION --- - -// -- CONFIGURATION: TYPES -- - -/// Config for glint -/// -pub type Config { - Config(pretty_help: Option(PrettyHelp), name: Option(String)) -} - -/// PrettyHelp defines the header colours to be used when styling help text -/// -pub type PrettyHelp { - PrettyHelp(usage: Colour, flags: Colour, subcommands: Colour) -} - -// -- CONFIGURATION: CONSTANTS -- - -/// Default config -/// -pub const default_config = Config(pretty_help: None, name: None) - -// -- CONFIGURATION: FUNCTIONS -- - -/// Add the provided config to the existing command tree -/// -pub fn with_config(glint: Glint(a), config: Config) -> Glint(a) { - Glint(..glint, config: config) -} - -/// Enable custom colours for help text headers -/// For a pre-made colouring use `default_pretty_help()` -/// -pub fn with_pretty_help(glint: Glint(a), pretty: PrettyHelp) -> Glint(a) { - Config(..glint.config, pretty_help: Some(pretty)) - |> with_config(glint, _) -} - -/// Disable custom colours for help text headers -/// -pub fn without_pretty_help(glint: Glint(a)) -> Glint(a) { - Config(..glint.config, pretty_help: None) - |> with_config(glint, _) -} - -pub fn with_name(glint: Glint(a), name: String) -> Glint(a) { - Config(..glint.config, name: Some(name)) - |> with_config(glint, _) -} - -// --- CORE --- - -// -- CORE: TYPES -- - -/// Glint container type for config and commands -/// -pub opaque type Glint(a) { - Glint(config: Config, cmd: CommandNode(a), global_flags: FlagMap) -} - -/// CommandNode contents -/// -pub opaque type Command(a) { - Command(do: Runner(a), flags: FlagMap, description: String) -} - -/// Input type for `Runner`. -/// -pub type CommandInput { - CommandInput(args: List(String), flags: FlagMap) -} - -/// Function type to be run by `glint`. -/// -pub type Runner(a) = - fn(CommandInput) -> a - -/// CommandNode tree representation. -/// -type CommandNode(a) { - CommandNode( - contents: Option(Command(a)), - subcommands: Map(String, CommandNode(a)), - ) -} - -/// Ok type for command execution -/// -pub type Out(a) { - /// Container for the command return value - Out(a) - /// Container for the generated help string - Help(String) -} - -/// Result type for command execution -/// -pub type CmdResult(a) = - Result(Out(a)) - -// -- CORE: BUILDER FUNCTIONS -- - -/// Creates a new command tree. -/// -pub fn new() -> Glint(a) { - Glint(config: default_config, cmd: empty_command(), global_flags: map.new()) -} - -/// Adds a new command to be run at the specified path. -/// -/// If the path is `[]`, the root command is set with the provided function and -/// flags. -/// -/// Note: all command paths are sanitized by stripping whitespace and removing any empty string elements. -/// -pub fn add( - to glint: Glint(a), - at path: List(String), - do contents: Command(a), -) -> Glint(a) { - Glint( - ..glint, - cmd: path - |> sanitize_path - |> do_add(to: glint.cmd, put: contents), - ) -} - -/// Recursive traversal of the command tree to find where to puth the provided command -/// -fn do_add( - to root: CommandNode(a), - at path: List(String), - put contents: Command(a), -) -> CommandNode(a) { - case path { - // update current command with provided contents - [] -> CommandNode(..root, contents: Some(contents)) - // continue down the path, creating empty command nodes along the way - [x, ..xs] -> - CommandNode( - ..root, - subcommands: { - use node <- map.update(root.subcommands, x) - node - |> option.lazy_unwrap(empty_command) - |> do_add(xs, contents) - }, - ) - } -} - -/// Helper for initializing empty commands -/// -fn empty_command() -> CommandNode(a) { - CommandNode(contents: None, subcommands: map.new()) -} - -/// Trim each path element and remove any resulting empty strings. -/// -fn sanitize_path(path: List(String)) -> List(String) { - path - |> list.map(string.trim) - |> list.filter(is_not_empty) -} - -/// Create a Command(a) from a Runner(a) -/// -pub fn command(do runner: Runner(a)) -> Command(a) { - Command(do: runner, flags: map.new(), description: "") -} - -/// Attach a description to a Command(a) -/// -pub fn description(cmd: Command(a), description: String) -> Command(a) { - Command(..cmd, description: description) -} - -/// add a `flag.Flag` to a `Command` -/// -pub fn flag( - cmd: Command(a), - at key: String, - of flag: flag.FlagBuilder(_), -) -> Command(a) { - Command(..cmd, flags: map.insert(cmd.flags, key, flag.build(flag))) -} - -/// Add a `flag.Flag to a `Command` when the flag name and builder are bundled as a #(String, flag.FlagBuilder(a)). -/// -/// This is merely a convenience function and calls `glint.flag` under the hood. -/// -pub fn flag_tuple( - cmd: Command(a), - with tup: #(String, flag.FlagBuilder(_)), -) -> Command(a) { - flag(cmd, tup.0, tup.1) -} - -/// Add multiple `Flag`s to a `Command`, note that this function uses `Flag` and not `FlagBuilder(_)`, so the user will need to call `flag.build` before providing the flags here. -/// -/// It is recommended to call `glint.flag` instead. -/// -pub fn flags(cmd: Command(a), with flags: List(#(String, Flag))) -> Command(a) { - use cmd, #(key, flag) <- list.fold(flags, cmd) - Command(..cmd, flags: map.insert(cmd.flags, key, flag)) -} - -/// Add global flags to the existing command tree -/// -pub fn global_flag( - glint: Glint(a), - at key: String, - of flag: flag.FlagBuilder(_), -) -> Glint(a) { - Glint( - ..glint, - global_flags: map.insert(glint.global_flags, key, flag.build(flag)), - ) -} - -/// Add global flags to the existing command tree. -/// -pub fn global_flag_tuple( - glint: Glint(a), - with tup: #(String, flag.FlagBuilder(_)), -) -> Glint(a) { - global_flag(glint, tup.0, tup.1) -} - -/// Add global flags to the existing command tree. -/// -/// Like `glint.flags`, this function requires `Flag`s insead of `FlagBuilder(_)`. -/// -/// It is recommended to use `glint.global_flag` instead. -/// -pub fn global_flags(glint: Glint(a), flags: List(#(String, Flag))) -> Glint(a) { - Glint( - ..glint, - global_flags: { - list.fold( - flags, - glint.global_flags, - fn(acc, tup) { map.insert(acc, tup.0, tup.1) }, - ) - }, - ) -} - -// -- CORE: EXECUTION FUNCTIONS -- - -/// Determines which command to run and executes it. -/// -/// Sets any provided flags if necessary. -/// -/// Each value prefixed with `--` is parsed as a flag. -/// -/// This function does not print its output and is mainly intended for use within `glint` itself. -/// If you would like to print or handle the output of a command please see the `run_and_handle` function. -/// -pub fn execute(glint: Glint(a), args: List(String)) -> CmdResult(a) { - // create help flag to check for - let help_flag = help_flag() - - // check if help flag is present - let #(help, args) = case list.pop(args, fn(s) { s == help_flag }) { - Ok(#(_, args)) -> #(True, args) - _ -> #(False, args) - } - - // split flags out from the args list - let #(flags, args) = list.partition(args, string.starts_with(_, flag.prefix)) - - // search for command and execute - do_execute(glint.cmd, glint.config, glint.global_flags, args, flags, help, []) -} - -/// Find which command to execute and run it with computed flags and args -/// -fn do_execute( - cmd: CommandNode(a), - config: Config, - global_flags: FlagMap, - args: List(String), - flags: List(String), - help: Bool, - command_path: List(String), -) -> CmdResult(a) { - case args { - // when there are no more available arguments - // and help flag has been passed, generate help message - [] if help -> - command_path - |> cmd_help(cmd, config, global_flags) - |> Help - |> Ok - - // when there are no more available arguments - // run the current command - [] -> execute_root(cmd, global_flags, [], flags) - - // when there are arguments remaining - // check if the next one is a subcommand of the current command - [arg, ..rest] -> - case map.get(cmd.subcommands, arg) { - // subcommand found, continue - Ok(cmd) -> - do_execute( - cmd, - config, - global_flags, - rest, - flags, - help, - [arg, ..command_path], - ) - // subcommand not found, but help flag has been passed - // generate and return help message - _ if help -> - command_path - |> cmd_help(cmd, config, global_flags) - |> Help - |> Ok - // subcommand not found, but help flag has not been passed - // execute the current command - _ -> execute_root(cmd, global_flags, args, flags) - } - } -} - -/// Executes the current root command. -/// -fn execute_root( - cmd: CommandNode(a), - global_flags: FlagMap, - args: List(String), - flag_inputs: List(String), -) -> CmdResult(a) { - case cmd.contents { - Some(contents) -> { - use new_flags <- result.try(list.try_fold( - over: flag_inputs, - from: map.merge(global_flags, contents.flags), - with: flag.update_flags, - )) - CommandInput(args, new_flags) - |> contents.do - |> Out - |> Ok - } - None -> snag.error("command not found") - } - |> snag.context("failed to run command") -} - -/// A wrapper for `execute` that prints any errors enountered or the help text if requested. -/// This function ignores any value returned by the command that was run. -/// If you would like to do something with the command output please see the run_and_handle function. -/// -pub fn run(from glint: Glint(a), for args: List(String)) -> Nil { - run_and_handle(from: glint, for: args, with: function.constant(Nil)) -} - -/// A wrapper for `execute` that prints any errors enountered or the help text if requested. -/// This function calls the provided handler with the value returned by the command that was run. -/// -pub fn run_and_handle( - from glint: Glint(a), - for args: List(String), - with handle: fn(a) -> _, -) -> Nil { - case execute(glint, args) { - Error(err) -> - err - |> snag.pretty_print - |> io.println - Ok(Help(help)) -> io.println(help) - Ok(Out(out)) -> { - handle(out) - Nil - } - } -} - -/// Default pretty help heading colouring -/// mint (r: 182, g: 255, b: 234) colour for usage -/// pink (r: 255, g: 175, b: 243) colour for flags -/// buttercup (r: 252, g: 226, b: 174) colour for subcommands -/// -pub fn default_pretty_help() -> PrettyHelp { - let assert Ok(usage_colour) = colour.from_rgb255(182, 255, 234) - let assert Ok(flags_colour) = colour.from_rgb255(255, 175, 243) - let assert Ok(subcommands_colour) = colour.from_rgb255(252, 226, 174) - - PrettyHelp( - usage: usage_colour, - flags: flags_colour, - subcommands: subcommands_colour, - ) -} - -// constants for setting up sections of the help message -const flags_heading = "FLAGS:" - -const subcommands_heading = "SUBCOMMANDS:" - -const usage_heading = "USAGE:" - -/// Helper for filtering out empty strings -/// -fn is_not_empty(s: String) -> Bool { - s != "" -} - -const help_flag_name = "help" - -const help_flag_message = "--help\t\t\tPrint help information" - -/// Function to create the help flag string -/// Exported for testing purposes only -/// -pub fn help_flag() -> String { - flag.prefix <> help_flag_name -} - -// -- HELP: FUNCTIONS -- - -fn wrap_with_space(s: String) -> String { - case s { - "" -> " " - _ -> " " <> s <> " " - } -} - -/// generate the usage help string for a command -fn usage_help(cmd_name: String, flags: FlagMap, config: Config) -> String { - let app_name = option.unwrap(config.name, "gleam run") - let flags = - flags - |> map.to_list - |> list.map(flag.flag_type_help) - |> list.sort(string.compare) - - let flag_sb = case flags { - [] -> sb.new() - _ -> - flags - |> list.intersperse(" ") - |> sb.from_strings() - |> sb.prepend(prefix: " [ ") - |> sb.append(suffix: " ]") - } - - [app_name, wrap_with_space(cmd_name), "[ ARGS ]"] - |> sb.from_strings - |> sb.append_builder(flag_sb) - |> sb.prepend( - config.pretty_help - |> option.map(fn(styling) { heading_style(usage_heading, styling.usage) }) - |> option.unwrap(usage_heading) <> "\n\t", - ) - |> sb.to_string -} - -/// generate the help text for a command -fn cmd_help( - path: List(String), - cmd: CommandNode(a), - config: Config, - global_flags: FlagMap, -) -> String { - // recreate the path of the current command - // reverse the path because it is created by prepending each section as do_execute walks down the tree - let name = - path - |> list.reverse - |> string.join(" ") - - let flags = - option.map(cmd.contents, fn(contents) { contents.flags }) - |> option.lazy_unwrap(map.new) - |> map.merge(global_flags, _) - - let flags_help_body = - config.pretty_help - |> option.map(fn(p) { heading_style(flags_heading, p.flags) }) - |> option.unwrap(flags_heading) <> "\n\t" <> string.join( - list.sort([help_flag_message, ..flag.flags_help(flags)], string.compare), - "\n\t", - ) - - let usage = usage_help(name, flags, config) - - let description = - cmd.contents - |> option.map(fn(contents) { contents.description }) - |> option.unwrap("") - - // create the header block from the name and description - let header_items = - [name, description] - |> list.filter(is_not_empty) - |> string.join("\n") - - // create the subcommands help block - let subcommands = case subcommands_help(cmd.subcommands) { - "" -> "" - subcommands_help_body -> - config.pretty_help - |> option.map(fn(p) { heading_style(subcommands_heading, p.subcommands) }) - |> option.unwrap(subcommands_heading) <> "\n\t" <> subcommands_help_body - } - - // join the resulting help blocks into the final help message - [header_items, usage, flags_help_body, subcommands] - |> list.filter(is_not_empty) - |> string.join("\n\n") -} - -// create the help text for subcommands -fn subcommands_help(cmds: Map(String, CommandNode(a))) -> String { - cmds - |> map.map_values(subcommand_help) - |> map.values - |> list.sort(string.compare) - |> string.join("\n\t") -} - -// generate the help text for a subcommand -fn subcommand_help(name: String, cmd: CommandNode(_)) -> String { - case cmd.contents { - None -> name - Some(contents) -> name <> "\t\t" <> contents.description - } -} - -/// Style heading text with the provided rgb colouring -/// this is only intended for use within glint itself. -/// -fn heading_style(heading: String, colour: Colour) -> String { - heading - |> ansi.bold - |> ansi.underline - |> ansi.italic - |> ansi.hex(colour.to_rgb_hex(colour)) - |> ansi.reset -} - -// -- DEPRECATED: STUBS -- - -/// DEPRECATED: use `glint.cmd` and related new functions instead to create a Command -/// -/// Create command stubs to be used in `add_command_from_stub` -/// -pub type Stub(a) { - Stub( - path: List(String), - run: Runner(a), - flags: List(#(String, Flag)), - description: String, - ) -} - -/// Add a command to the root given a stub -/// -@deprecated("use `glint.cmd` and related new functions instead to create a Command") -pub fn add_command_from_stub(to glint: Glint(a), with stub: Stub(a)) -> Glint(a) { - add( - to: glint, - at: stub.path, - do: command(stub.run) - |> flags(stub.flags) - |> description(stub.description), - ) -} diff --git a/aoc2023/build/packages/glint/src/glint/flag.gleam b/aoc2023/build/packages/glint/src/glint/flag.gleam deleted file mode 100644 index 0a6cae1..0000000 --- a/aoc2023/build/packages/glint/src/glint/flag.gleam +++ /dev/null @@ -1,478 +0,0 @@ -import gleam/map -import gleam/string -import gleam/result -import gleam/int -import gleam/list -import gleam/float -import snag.{type Result, type Snag} -import gleam/option.{type Option, None, Some} -import glint/flag/constraint.{type Constraint} -import gleam - -/// Flag inputs must start with this prefix -/// -pub const prefix = "--" - -/// The separation character for flag names and their values -const delimiter = "=" - -/// Supported flag types. -/// -pub type Value { - /// Boolean flags, to be passed in as `--flag=true` or `--flag=false`. - /// Can be toggled by omitting the desired value like `--flag`. - /// Toggling will negate the existing value. - /// - B(Internal(Bool)) - - /// Int flags, to be passed in as `--flag=1` - /// - I(Internal(Int)) - - /// List(Int) flags, to be passed in as `--flag=1,2,3` - /// - LI(Internal(List(Int))) - - /// Float flags, to be passed in as `--flag=1.0` - /// - F(Internal(Float)) - - /// List(Float) flags, to be passed in as `--flag=1.0,2.0` - /// - LF(Internal(List(Float))) - - /// String flags, to be passed in as `--flag=hello` - /// - S(Internal(String)) - - /// List(String) flags, to be passed in as `--flag=hello,world` - /// - LS(Internal(List(String))) -} - -/// A type that facilitates the creation of `Flag`s -/// -pub opaque type FlagBuilder(a) { - FlagBuilder( - desc: Description, - parser: Parser(a, Snag), - value: fn(Internal(a)) -> Value, - default: Option(a), - ) -} - -/// An internal representation of flag contents -/// -pub opaque type Internal(a) { - Internal(value: Option(a), parser: Parser(a, Snag)) -} - -// Builder initializers - -type Parser(a, b) = - fn(String) -> gleam.Result(a, b) - -/// initialise an int flag builder -/// -pub fn int() -> FlagBuilder(Int) { - use input <- new(I) - input - |> int.parse - |> result.replace_error(cannot_parse(input, "int")) -} - -/// initialise an int list flag builder -/// -pub fn int_list() -> FlagBuilder(List(Int)) { - use input <- new(LI) - input - |> string.split(",") - |> list.try_map(int.parse) - |> result.replace_error(cannot_parse(input, "int list")) -} - -/// initialise a float flag builder -/// -pub fn float() -> FlagBuilder(Float) { - use input <- new(F) - input - |> float.parse - |> result.replace_error(cannot_parse(input, "float")) -} - -/// initialise a float list flag builder -/// -pub fn float_list() -> FlagBuilder(List(Float)) { - use input <- new(LF) - input - |> string.split(",") - |> list.try_map(float.parse) - |> result.replace_error(cannot_parse(input, "float list")) -} - -/// initialise a string flag builder -/// -pub fn string() -> FlagBuilder(String) { - new(S, fn(s) { Ok(s) }) -} - -/// intitialise a string list flag builder -/// -pub fn string_list() -> FlagBuilder(List(String)) { - use input <- new(LS) - input - |> string.split(",") - |> Ok -} - -/// initialise a bool flag builder -/// -pub fn bool() -> FlagBuilder(Bool) { - use input <- new(B) - case string.lowercase(input) { - "true" | "t" -> Ok(True) - "false" | "f" -> Ok(False) - _ -> Error(cannot_parse(input, "bool")) - } -} - -/// initialize custom builders using a Value constructor and a parsing function -/// -fn new(valuer: fn(Internal(a)) -> Value, p: Parser(a, Snag)) -> FlagBuilder(a) { - FlagBuilder(desc: "", parser: p, value: valuer, default: None) -} - -/// convert a FlagBuilder(a) into its corresponding Flag representation -/// -pub fn build(fb: FlagBuilder(a)) -> Flag { - Flag( - value: fb.value(Internal(value: fb.default, parser: fb.parser)), - description: fb.desc, - ) -} - -/// attach a constraint to a `Flag` -/// -pub fn constraint( - builder: FlagBuilder(a), - constraint: Constraint(a), -) -> FlagBuilder(a) { - FlagBuilder( - ..builder, - parser: wrap_with_constraint(builder.parser, constraint), - ) -} - -/// attach a Constraint(a) to a Parser(a,Snag) -/// this function should not be used directly unless -fn wrap_with_constraint( - p: Parser(a, Snag), - constraint: Constraint(a), -) -> Parser(a, Snag) { - fn(input: String) -> Result(a) { attempt(p(input), constraint) } -} - -fn attempt( - val: gleam.Result(a, e), - f: fn(a) -> gleam.Result(_, e), -) -> gleam.Result(a, e) { - use a <- result.try(val) - result.replace(f(a), a) -} - -/// Flag descriptions -/// -pub type Description = - String - -/// Flag data and descriptions -/// -pub type Flag { - Flag(value: Value, description: Description) -} - -/// attach a description to a `Flag` -/// -pub fn description( - for builder: FlagBuilder(a), - of description: Description, -) -> FlagBuilder(a) { - FlagBuilder(..builder, desc: description) -} - -/// Set the default value for a flag `Value` -/// -pub fn default(for builder: FlagBuilder(a), of default: a) -> FlagBuilder(a) { - FlagBuilder(..builder, default: Some(default)) -} - -/// Associate flag names to their current values. -/// -pub type Map = - map.Map(String, Flag) - -/// Convert a list of flags to a Map. -/// -pub fn build_map(flags: List(#(String, Flag))) -> Map { - map.from_list(flags) -} - -/// Updates a flag value, ensuring that the new value can satisfy the required type. -/// Assumes that all flag inputs passed in start with -- -/// This function is only intended to be used from glint.execute_root -/// -pub fn update_flags(in flags: Map, with flag_input: String) -> Result(Map) { - let flag_input = string.drop_left(flag_input, string.length(prefix)) - - case string.split_once(flag_input, delimiter) { - Ok(data) -> update_flag_value(flags, data) - Error(_) -> attempt_toggle_flag(flags, flag_input) - } -} - -fn update_flag_value(in flags: Map, with data: #(String, String)) -> Result(Map) { - let #(key, input) = data - use contents <- result.try(access(flags, key)) - use value <- result.map( - compute_flag(with: input, given: contents.value) - |> result.map_error(layer_invalid_flag(_, key)), - ) - map.insert(flags, key, Flag(..contents, value: value)) -} - -fn attempt_toggle_flag(in flags: Map, at key: String) -> Result(Map) { - use contents <- result.try(access(flags, key)) - case contents.value { - B(Internal(None, ..) as internal) -> - Internal(..internal, value: Some(True)) - |> B - |> fn(val) { Flag(..contents, value: val) } - |> map.insert(into: flags, for: key) - |> Ok() - B(Internal(Some(val), ..) as internal) -> - Internal(..internal, value: Some(!val)) - |> B - |> fn(val) { Flag(..contents, value: val) } - |> map.insert(into: flags, for: key) - |> Ok() - _ -> Error(no_value_flag_err(key)) - } -} - -fn access_type_error(flag_type) { - snag.error("cannot access flag as " <> flag_type) -} - -fn flag_not_provided_error() { - snag.error("no value provided") -} - -fn construct_value( - input: String, - internal: Internal(a), - constructor: fn(Internal(a)) -> Value, -) -> Result(Value) { - use val <- result.map(internal.parser(input)) - constructor(Internal(..internal, value: Some(val))) -} - -/// Computes the new flag value given the input and the expected flag type -/// -fn compute_flag(with input: String, given current: Value) -> Result(Value) { - input - |> case current { - I(internal) -> construct_value(_, internal, I) - LI(internal) -> construct_value(_, internal, LI) - F(internal) -> construct_value(_, internal, F) - LF(internal) -> construct_value(_, internal, LF) - S(internal) -> construct_value(_, internal, S) - LS(internal) -> construct_value(_, internal, LS) - B(internal) -> construct_value(_, internal, B) - } - |> snag.context("failed to compute value for flag") -} - -// Error creation and manipulation functions -fn layer_invalid_flag(err: Snag, flag: String) -> Snag { - snag.layer(err, "invalid flag '" <> flag <> "'") -} - -fn no_value_flag_err(flag_input: String) -> Snag { - { "flag '" <> flag_input <> "' has no assigned value" } - |> snag.new() - |> layer_invalid_flag(flag_input) -} - -fn undefined_flag_err(key: String) -> Snag { - "flag provided but not defined" - |> snag.new() - |> layer_invalid_flag(key) -} - -fn cannot_parse(with value: String, is kind: String) -> Snag { - { "cannot parse value '" <> value <> "' as " <> kind } - |> snag.new() -} - -// Help Message Functions -/// Generate the help message contents for a single flag -/// -pub fn flag_type_help(flag: #(String, Flag)) { - let #(name, contents) = flag - let kind = case contents.value { - I(_) -> "INT" - B(_) -> "BOOL" - F(_) -> "FLOAT" - LF(_) -> "FLOAT_LIST" - LI(_) -> "INT_LIST" - LS(_) -> "STRING_LIST" - S(_) -> "STRING" - } - - prefix <> name <> delimiter <> "<" <> kind <> ">" -} - -/// Generate help message line for a single flag -/// -fn flag_help(flag: #(String, Flag)) -> String { - flag_type_help(flag) <> "\t\t" <> { flag.1 }.description -} - -/// Generate help messages for all flags -/// -pub fn flags_help(flags: Map) -> List(String) { - flags - |> map.to_list - |> list.map(flag_help) -} - -// -- FLAG ACCESS FUNCTIONS -- - -/// Access the contents for the associated flag -/// -fn access(flags: Map, name: String) -> Result(Flag) { - map.get(flags, name) - |> result.replace_error(undefined_flag_err(name)) -} - -fn get_value( - from flags: Map, - at key: String, - expecting kind: fn(Flag) -> Result(a), -) -> Result(a) { - access(flags, key) - |> result.try(kind) - |> snag.context("failed to retrieve value for flag '" <> key <> "'") -} - -/// Gets the current value for the provided int flag -/// -pub fn get_int_value(from flag: Flag) -> Result(Int) { - case flag.value { - I(Internal(value: Some(val), ..)) -> Ok(val) - I(Internal(value: None, ..)) -> flag_not_provided_error() - _ -> access_type_error("int") - } -} - -/// Gets the current value for the associated int flag -/// -pub fn get_int(from flags: Map, for name: String) -> Result(Int) { - get_value(flags, name, get_int_value) -} - -/// Gets the current value for the provided ints flag -/// -pub fn get_ints_value(from flag: Flag) -> Result(List(Int)) { - case flag.value { - LI(Internal(value: Some(val), ..)) -> Ok(val) - LI(Internal(value: None, ..)) -> flag_not_provided_error() - _ -> access_type_error("int list") - } -} - -/// Gets the current value for the associated ints flag -/// -pub fn get_ints(from flags: Map, for name: String) -> Result(List(Int)) { - get_value(flags, name, get_ints_value) -} - -/// Gets the current value for the provided bool flag -/// -pub fn get_bool_value(from flag: Flag) -> Result(Bool) { - case flag.value { - B(Internal(Some(val), ..)) -> Ok(val) - B(Internal(None, ..)) -> flag_not_provided_error() - _ -> access_type_error("bool") - } -} - -/// Gets the current value for the associated bool flag -/// -pub fn get_bool(from flags: Map, for name: String) -> Result(Bool) { - get_value(flags, name, get_bool_value) -} - -/// Gets the current value for the provided string flag -/// -pub fn get_string_value(from flag: Flag) -> Result(String) { - case flag.value { - S(Internal(value: Some(val), ..)) -> Ok(val) - S(Internal(value: None, ..)) -> flag_not_provided_error() - _ -> access_type_error("string") - } -} - -/// Gets the current value for the associated string flag -/// -pub fn get_string(from flags: Map, for name: String) -> Result(String) { - get_value(flags, name, get_string_value) -} - -/// Gets the current value for the provided strings flag -/// -pub fn get_strings_value(from flag: Flag) -> Result(List(String)) { - case flag.value { - LS(Internal(value: Some(val), ..)) -> Ok(val) - LS(Internal(value: None, ..)) -> flag_not_provided_error() - _ -> access_type_error("string list") - } -} - -/// Gets the current value for the associated strings flag -/// -pub fn get_strings(from flags: Map, for name: String) -> Result(List(String)) { - get_value(flags, name, get_strings_value) -} - -/// Gets the current value for the provided float flag -/// -pub fn get_float_value(from flag: Flag) -> Result(Float) { - case flag.value { - F(Internal(value: Some(val), ..)) -> Ok(val) - F(Internal(value: None, ..)) -> flag_not_provided_error() - _ -> access_type_error("float") - } -} - -/// Gets the current value for the associated float flag -/// -pub fn get_float(from flags: Map, for name: String) -> Result(Float) { - get_value(flags, name, get_float_value) -} - -/// Gets the current value for the provided floats flag -/// -pub fn get_floats_value(from flag: Flag) -> Result(List(Float)) { - case flag.value { - LF(Internal(value: Some(val), ..)) -> Ok(val) - LF(Internal(value: None, ..)) -> flag_not_provided_error() - _ -> access_type_error("float list") - } -} - -/// Gets the current value for the associated floats flag -/// -pub fn get_floats(from flags: Map, for name: String) -> Result(List(Float)) { - get_value(flags, name, get_floats_value) -} diff --git a/aoc2023/build/packages/glint/src/glint/flag/constraint.gleam b/aoc2023/build/packages/glint/src/glint/flag/constraint.gleam deleted file mode 100644 index e474bc2..0000000 --- a/aoc2023/build/packages/glint/src/glint/flag/constraint.gleam +++ /dev/null @@ -1,66 +0,0 @@ -import gleam/list -import gleam/result -import gleam/string -import gleam/set -import snag.{type Result} - -/// Constraint type for verifying flag values -/// -pub type Constraint(a) = - fn(a) -> Result(Nil) - -/// one_of returns a Constraint that ensures the parsed flag value is -/// one of the allowed values. -/// -pub fn one_of(allowed: List(a)) -> Constraint(a) { - let allowed_set = set.from_list(allowed) - fn(val: a) -> Result(Nil) { - case set.contains(allowed_set, val) { - True -> Ok(Nil) - False -> - snag.error( - "invalid value '" <> string.inspect(val) <> "', must be one of: [" <> { - allowed - |> list.map(fn(a) { "'" <> string.inspect(a) <> "'" }) - |> string.join(", ") <> "]" - }, - ) - } - } -} - -/// none_of returns a Constraint that ensures the parsed flag value is not one of the disallowed values. -/// -pub fn none_of(disallowed: List(a)) -> Constraint(a) { - let disallowed_set = set.from_list(disallowed) - fn(val: a) -> Result(Nil) { - case set.contains(disallowed_set, val) { - False -> Ok(Nil) - True -> - snag.error( - "invalid value '" <> string.inspect(val) <> "', must not be one of: [" <> { - { - disallowed - |> list.map(fn(a) { "'" <> string.inspect(a) <> "'" }) - |> string.join(", ") <> "]" - } - }, - ) - } - } -} - -/// each is a convenience function for applying a Constraint(a) to a List(a). -/// This is useful because the default behaviour for constraints on lists is that they will apply to the list as a whole. -/// -/// For example, to apply one_of to all items in a `List(Int)`: -/// ```gleam -/// [1, 2, 3, 4] |> one_of |> each -/// ``` -pub fn each(constraint: Constraint(a)) -> Constraint(List(a)) { - fn(l: List(a)) -> Result(Nil) { - l - |> list.try_map(constraint) - |> result.replace(Nil) - } -} diff --git a/aoc2023/build/packages/glint/src/glint@flag.erl b/aoc2023/build/packages/glint/src/glint@flag.erl deleted file mode 100644 index bcce6db..0000000 --- a/aoc2023/build/packages/glint/src/glint@flag.erl +++ /dev/null @@ -1,523 +0,0 @@ --module(glint@flag). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([string/0, string_list/0, build/1, constraint/2, description/2, default/2, build_map/1, int/0, int_list/0, float/0, float_list/0, bool/0, flag_type_help/1, flags_help/1, update_flags/2, get_int_value/1, get_int/2, get_ints_value/1, get_ints/2, get_bool_value/1, get_bool/2, get_string_value/1, get_string/2, get_strings_value/1, get_strings/2, get_float_value/1, get_float/2, get_floats_value/1, get_floats/2]). --export_type([value/0, flag_builder/1, internal/1, flag/0]). - --type value() :: {b, internal(boolean())} | - {i, internal(integer())} | - {li, internal(list(integer()))} | - {f, internal(float())} | - {lf, internal(list(float()))} | - {s, internal(binary())} | - {ls, internal(list(binary()))}. - --opaque flag_builder(FTZ) :: {flag_builder, - binary(), - fun((binary()) -> {ok, FTZ} | {error, snag:snag()}), - fun((internal(FTZ)) -> value()), - gleam@option:option(FTZ)}. - --opaque internal(FUA) :: {internal, - gleam@option:option(FUA), - fun((binary()) -> {ok, FUA} | {error, snag:snag()})}. - --type flag() :: {flag, value(), binary()}. - --spec new( - fun((internal(FUR)) -> value()), - fun((binary()) -> {ok, FUR} | {error, snag:snag()}) -) -> flag_builder(FUR). -new(Valuer, P) -> - {flag_builder, <<""/utf8>>, P, Valuer, none}. - --spec string() -> flag_builder(binary()). -string() -> - new(fun(Field@0) -> {s, Field@0} end, fun(S) -> {ok, S} end). - --spec string_list() -> flag_builder(list(binary())). -string_list() -> - new(fun(Field@0) -> {ls, Field@0} end, fun(Input) -> _pipe = Input, - _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>), - {ok, _pipe@1} end). - --spec build(flag_builder(any())) -> flag(). -build(Fb) -> - {flag, - (erlang:element(4, Fb))( - {internal, erlang:element(5, Fb), erlang:element(3, Fb)} - ), - erlang:element(2, Fb)}. - --spec attempt( - {ok, FVI} | {error, FVJ}, - fun((FVI) -> {ok, any()} | {error, FVJ}) -) -> {ok, FVI} | {error, FVJ}. -attempt(Val, F) -> - gleam@result:'try'(Val, fun(A) -> gleam@result:replace(F(A), A) end). - --spec wrap_with_constraint( - fun((binary()) -> {ok, FVC} | {error, snag:snag()}), - fun((FVC) -> {ok, nil} | {error, snag:snag()}) -) -> fun((binary()) -> {ok, FVC} | {error, snag:snag()}). -wrap_with_constraint(P, Constraint) -> - fun(Input) -> attempt(P(Input), Constraint) end. - --spec constraint( - flag_builder(FUY), - fun((FUY) -> {ok, nil} | {error, snag:snag()}) -) -> flag_builder(FUY). -constraint(Builder, Constraint) -> - erlang:setelement( - 3, - Builder, - wrap_with_constraint(erlang:element(3, Builder), Constraint) - ). - --spec description(flag_builder(FVR), binary()) -> flag_builder(FVR). -description(Builder, Description) -> - erlang:setelement(2, Builder, Description). - --spec default(flag_builder(FVU), FVU) -> flag_builder(FVU). -default(Builder, Default) -> - erlang:setelement(5, Builder, {some, Default}). - --spec build_map(list({binary(), flag()})) -> gleam@map:map_(binary(), flag()). -build_map(Flags) -> - gleam@map:from_list(Flags). - --spec access_type_error(binary()) -> {ok, any()} | {error, snag:snag()}. -access_type_error(Flag_type) -> - snag:error(<<"cannot access flag as "/utf8, Flag_type/binary>>). - --spec flag_not_provided_error() -> {ok, any()} | {error, snag:snag()}. -flag_not_provided_error() -> - snag:error(<<"no value provided"/utf8>>). - --spec construct_value(binary(), internal(FWE), fun((internal(FWE)) -> value())) -> {ok, - value()} | - {error, snag:snag()}. -construct_value(Input, Internal, Constructor) -> - gleam@result:map( - (erlang:element(3, Internal))(Input), - fun(Val) -> Constructor(erlang:setelement(2, Internal, {some, Val})) end - ). - --spec compute_flag(binary(), value()) -> {ok, value()} | {error, snag:snag()}. -compute_flag(Input, Current) -> - _pipe = Input, - _pipe@1 = case Current of - {i, Internal} -> - fun(_capture) -> - construct_value( - _capture, - Internal, - fun(Field@0) -> {i, Field@0} end - ) - end; - - {li, Internal@1} -> - fun(_capture@1) -> - construct_value( - _capture@1, - Internal@1, - fun(Field@0) -> {li, Field@0} end - ) - end; - - {f, Internal@2} -> - fun(_capture@2) -> - construct_value( - _capture@2, - Internal@2, - fun(Field@0) -> {f, Field@0} end - ) - end; - - {lf, Internal@3} -> - fun(_capture@3) -> - construct_value( - _capture@3, - Internal@3, - fun(Field@0) -> {lf, Field@0} end - ) - end; - - {s, Internal@4} -> - fun(_capture@4) -> - construct_value( - _capture@4, - Internal@4, - fun(Field@0) -> {s, Field@0} end - ) - end; - - {ls, Internal@5} -> - fun(_capture@5) -> - construct_value( - _capture@5, - Internal@5, - fun(Field@0) -> {ls, Field@0} end - ) - end; - - {b, Internal@6} -> - fun(_capture@6) -> - construct_value( - _capture@6, - Internal@6, - fun(Field@0) -> {b, Field@0} end - ) - end - end(_pipe), - snag:context(_pipe@1, <<"failed to compute value for flag"/utf8>>). - --spec layer_invalid_flag(snag:snag(), binary()) -> snag:snag(). -layer_invalid_flag(Err, Flag) -> - snag:layer(Err, <<<<"invalid flag '"/utf8, Flag/binary>>/binary, "'"/utf8>>). - --spec no_value_flag_err(binary()) -> snag:snag(). -no_value_flag_err(Flag_input) -> - _pipe = (<<<<"flag '"/utf8, Flag_input/binary>>/binary, - "' has no assigned value"/utf8>>), - _pipe@1 = snag:new(_pipe), - layer_invalid_flag(_pipe@1, Flag_input). - --spec undefined_flag_err(binary()) -> snag:snag(). -undefined_flag_err(Key) -> - _pipe = <<"flag provided but not defined"/utf8>>, - _pipe@1 = snag:new(_pipe), - layer_invalid_flag(_pipe@1, Key). - --spec cannot_parse(binary(), binary()) -> snag:snag(). -cannot_parse(Value, Kind) -> - _pipe = (<<<<<<"cannot parse value '"/utf8, Value/binary>>/binary, - "' as "/utf8>>/binary, - Kind/binary>>), - snag:new(_pipe). - --spec int() -> flag_builder(integer()). -int() -> - new(fun(Field@0) -> {i, Field@0} end, fun(Input) -> _pipe = Input, - _pipe@1 = gleam@int:parse(_pipe), - gleam@result:replace_error( - _pipe@1, - cannot_parse(Input, <<"int"/utf8>>) - ) end). - --spec int_list() -> flag_builder(list(integer())). -int_list() -> - new(fun(Field@0) -> {li, Field@0} end, fun(Input) -> _pipe = Input, - _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>), - _pipe@2 = gleam@list:try_map(_pipe@1, fun gleam@int:parse/1), - gleam@result:replace_error( - _pipe@2, - cannot_parse(Input, <<"int list"/utf8>>) - ) end). - --spec float() -> flag_builder(float()). -float() -> - new(fun(Field@0) -> {f, Field@0} end, fun(Input) -> _pipe = Input, - _pipe@1 = gleam@float:parse(_pipe), - gleam@result:replace_error( - _pipe@1, - cannot_parse(Input, <<"float"/utf8>>) - ) end). - --spec float_list() -> flag_builder(list(float())). -float_list() -> - new(fun(Field@0) -> {lf, Field@0} end, fun(Input) -> _pipe = Input, - _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>), - _pipe@2 = gleam@list:try_map(_pipe@1, fun gleam@float:parse/1), - gleam@result:replace_error( - _pipe@2, - cannot_parse(Input, <<"float list"/utf8>>) - ) end). - --spec bool() -> flag_builder(boolean()). -bool() -> - new( - fun(Field@0) -> {b, Field@0} end, - fun(Input) -> case gleam@string:lowercase(Input) of - <<"true"/utf8>> -> - {ok, true}; - - <<"t"/utf8>> -> - {ok, true}; - - <<"false"/utf8>> -> - {ok, false}; - - <<"f"/utf8>> -> - {ok, false}; - - _ -> - {error, cannot_parse(Input, <<"bool"/utf8>>)} - end end - ). - --spec flag_type_help({binary(), flag()}) -> binary(). -flag_type_help(Flag) -> - {Name, Contents} = Flag, - Kind = case erlang:element(2, Contents) of - {i, _} -> - <<"INT"/utf8>>; - - {b, _} -> - <<"BOOL"/utf8>>; - - {f, _} -> - <<"FLOAT"/utf8>>; - - {lf, _} -> - <<"FLOAT_LIST"/utf8>>; - - {li, _} -> - <<"INT_LIST"/utf8>>; - - {ls, _} -> - <<"STRING_LIST"/utf8>>; - - {s, _} -> - <<"STRING"/utf8>> - end, - <<<<<<<<<<"--"/utf8, Name/binary>>/binary, "="/utf8>>/binary, "<"/utf8>>/binary, - Kind/binary>>/binary, - ">"/utf8>>. - --spec flag_help({binary(), flag()}) -> binary(). -flag_help(Flag) -> - <<<<(flag_type_help(Flag))/binary, "\t\t"/utf8>>/binary, - (erlang:element(3, (erlang:element(2, Flag))))/binary>>. - --spec flags_help(gleam@map:map_(binary(), flag())) -> list(binary()). -flags_help(Flags) -> - _pipe = Flags, - _pipe@1 = gleam@map:to_list(_pipe), - gleam@list:map(_pipe@1, fun flag_help/1). - --spec access(gleam@map:map_(binary(), flag()), binary()) -> {ok, flag()} | - {error, snag:snag()}. -access(Flags, Name) -> - _pipe = gleam@map:get(Flags, Name), - gleam@result:replace_error(_pipe, undefined_flag_err(Name)). - --spec update_flag_value(gleam@map:map_(binary(), flag()), {binary(), binary()}) -> {ok, - gleam@map:map_(binary(), flag())} | - {error, snag:snag()}. -update_flag_value(Flags, Data) -> - {Key, Input} = Data, - gleam@result:'try'( - access(Flags, Key), - fun(Contents) -> - gleam@result:map( - begin - _pipe = compute_flag(Input, erlang:element(2, Contents)), - gleam@result:map_error( - _pipe, - fun(_capture) -> layer_invalid_flag(_capture, Key) end - ) - end, - fun(Value) -> - gleam@map:insert( - Flags, - Key, - erlang:setelement(2, Contents, Value) - ) - end - ) - end - ). - --spec attempt_toggle_flag(gleam@map:map_(binary(), flag()), binary()) -> {ok, - gleam@map:map_(binary(), flag())} | - {error, snag:snag()}. -attempt_toggle_flag(Flags, Key) -> - gleam@result:'try'( - access(Flags, Key), - fun(Contents) -> case erlang:element(2, Contents) of - {b, {internal, none, _} = Internal} -> - _pipe = erlang:setelement(2, Internal, {some, true}), - _pipe@1 = {b, _pipe}, - _pipe@2 = (fun(Val) -> - erlang:setelement(2, Contents, Val) - end)(_pipe@1), - _pipe@3 = gleam@map:insert(Flags, Key, _pipe@2), - {ok, _pipe@3}; - - {b, {internal, {some, Val@1}, _} = Internal@1} -> - _pipe@4 = erlang:setelement( - 2, - Internal@1, - {some, not Val@1} - ), - _pipe@5 = {b, _pipe@4}, - _pipe@6 = (fun(Val@2) -> - erlang:setelement(2, Contents, Val@2) - end)(_pipe@5), - _pipe@7 = gleam@map:insert(Flags, Key, _pipe@6), - {ok, _pipe@7}; - - _ -> - {error, no_value_flag_err(Key)} - end end - ). - --spec update_flags(gleam@map:map_(binary(), flag()), binary()) -> {ok, - gleam@map:map_(binary(), flag())} | - {error, snag:snag()}. -update_flags(Flags, Flag_input) -> - Flag_input@1 = gleam@string:drop_left( - Flag_input, - gleam@string:length(<<"--"/utf8>>) - ), - case gleam@string:split_once(Flag_input@1, <<"="/utf8>>) of - {ok, Data} -> - update_flag_value(Flags, Data); - - {error, _} -> - attempt_toggle_flag(Flags, Flag_input@1) - end. - --spec get_value( - gleam@map:map_(binary(), flag()), - binary(), - fun((flag()) -> {ok, FWM} | {error, snag:snag()}) -) -> {ok, FWM} | {error, snag:snag()}. -get_value(Flags, Key, Kind) -> - _pipe = access(Flags, Key), - _pipe@1 = gleam@result:'try'(_pipe, Kind), - snag:context( - _pipe@1, - <<<<"failed to retrieve value for flag '"/utf8, Key/binary>>/binary, - "'"/utf8>> - ). - --spec get_int_value(flag()) -> {ok, integer()} | {error, snag:snag()}. -get_int_value(Flag) -> - case erlang:element(2, Flag) of - {i, {internal, {some, Val}, _}} -> - {ok, Val}; - - {i, {internal, none, _}} -> - flag_not_provided_error(); - - _ -> - access_type_error(<<"int"/utf8>>) - end. - --spec get_int(gleam@map:map_(binary(), flag()), binary()) -> {ok, integer()} | - {error, snag:snag()}. -get_int(Flags, Name) -> - get_value(Flags, Name, fun get_int_value/1). - --spec get_ints_value(flag()) -> {ok, list(integer())} | {error, snag:snag()}. -get_ints_value(Flag) -> - case erlang:element(2, Flag) of - {li, {internal, {some, Val}, _}} -> - {ok, Val}; - - {li, {internal, none, _}} -> - flag_not_provided_error(); - - _ -> - access_type_error(<<"int list"/utf8>>) - end. - --spec get_ints(gleam@map:map_(binary(), flag()), binary()) -> {ok, - list(integer())} | - {error, snag:snag()}. -get_ints(Flags, Name) -> - get_value(Flags, Name, fun get_ints_value/1). - --spec get_bool_value(flag()) -> {ok, boolean()} | {error, snag:snag()}. -get_bool_value(Flag) -> - case erlang:element(2, Flag) of - {b, {internal, {some, Val}, _}} -> - {ok, Val}; - - {b, {internal, none, _}} -> - flag_not_provided_error(); - - _ -> - access_type_error(<<"bool"/utf8>>) - end. - --spec get_bool(gleam@map:map_(binary(), flag()), binary()) -> {ok, boolean()} | - {error, snag:snag()}. -get_bool(Flags, Name) -> - get_value(Flags, Name, fun get_bool_value/1). - --spec get_string_value(flag()) -> {ok, binary()} | {error, snag:snag()}. -get_string_value(Flag) -> - case erlang:element(2, Flag) of - {s, {internal, {some, Val}, _}} -> - {ok, Val}; - - {s, {internal, none, _}} -> - flag_not_provided_error(); - - _ -> - access_type_error(<<"string"/utf8>>) - end. - --spec get_string(gleam@map:map_(binary(), flag()), binary()) -> {ok, binary()} | - {error, snag:snag()}. -get_string(Flags, Name) -> - get_value(Flags, Name, fun get_string_value/1). - --spec get_strings_value(flag()) -> {ok, list(binary())} | {error, snag:snag()}. -get_strings_value(Flag) -> - case erlang:element(2, Flag) of - {ls, {internal, {some, Val}, _}} -> - {ok, Val}; - - {ls, {internal, none, _}} -> - flag_not_provided_error(); - - _ -> - access_type_error(<<"string list"/utf8>>) - end. - --spec get_strings(gleam@map:map_(binary(), flag()), binary()) -> {ok, - list(binary())} | - {error, snag:snag()}. -get_strings(Flags, Name) -> - get_value(Flags, Name, fun get_strings_value/1). - --spec get_float_value(flag()) -> {ok, float()} | {error, snag:snag()}. -get_float_value(Flag) -> - case erlang:element(2, Flag) of - {f, {internal, {some, Val}, _}} -> - {ok, Val}; - - {f, {internal, none, _}} -> - flag_not_provided_error(); - - _ -> - access_type_error(<<"float"/utf8>>) - end. - --spec get_float(gleam@map:map_(binary(), flag()), binary()) -> {ok, float()} | - {error, snag:snag()}. -get_float(Flags, Name) -> - get_value(Flags, Name, fun get_float_value/1). - --spec get_floats_value(flag()) -> {ok, list(float())} | {error, snag:snag()}. -get_floats_value(Flag) -> - case erlang:element(2, Flag) of - {lf, {internal, {some, Val}, _}} -> - {ok, Val}; - - {lf, {internal, none, _}} -> - flag_not_provided_error(); - - _ -> - access_type_error(<<"float list"/utf8>>) - end. - --spec get_floats(gleam@map:map_(binary(), flag()), binary()) -> {ok, - list(float())} | - {error, snag:snag()}. -get_floats(Flags, Name) -> - get_value(Flags, Name, fun get_floats_value/1). diff --git a/aoc2023/build/packages/glint/src/glint@flag@constraint.erl b/aoc2023/build/packages/glint/src/glint@flag@constraint.erl deleted file mode 100644 index 2978be0..0000000 --- a/aoc2023/build/packages/glint/src/glint@flag@constraint.erl +++ /dev/null @@ -1,68 +0,0 @@ --module(glint@flag@constraint). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([one_of/1, none_of/1, each/1]). - --spec one_of(list(FSI)) -> fun((FSI) -> {ok, nil} | {error, snag:snag()}). -one_of(Allowed) -> - Allowed_set = gleam@set:from_list(Allowed), - fun(Val) -> case gleam@set:contains(Allowed_set, Val) of - true -> - {ok, nil}; - - false -> - snag:error( - <<<<<<"invalid value '"/utf8, - (gleam@string:inspect(Val))/binary>>/binary, - "', must be one of: ["/utf8>>/binary, - ((<<(begin - _pipe = Allowed, - _pipe@1 = gleam@list:map( - _pipe, - fun(A) -> - <<<<"'"/utf8, - (gleam@string:inspect(A))/binary>>/binary, - "'"/utf8>> - end - ), - gleam@string:join(_pipe@1, <<", "/utf8>>) - end)/binary, - "]"/utf8>>))/binary>> - ) - end end. - --spec none_of(list(FSL)) -> fun((FSL) -> {ok, nil} | {error, snag:snag()}). -none_of(Disallowed) -> - Disallowed_set = gleam@set:from_list(Disallowed), - fun(Val) -> case gleam@set:contains(Disallowed_set, Val) of - false -> - {ok, nil}; - - true -> - snag:error( - <<<<<<"invalid value '"/utf8, - (gleam@string:inspect(Val))/binary>>/binary, - "', must not be one of: ["/utf8>>/binary, - (((<<(begin - _pipe = Disallowed, - _pipe@1 = gleam@list:map( - _pipe, - fun(A) -> - <<<<"'"/utf8, - (gleam@string:inspect(A))/binary>>/binary, - "'"/utf8>> - end - ), - gleam@string:join(_pipe@1, <<", "/utf8>>) - end)/binary, - "]"/utf8>>)))/binary>> - ) - end end. - --spec each(fun((FSO) -> {ok, nil} | {error, snag:snag()})) -> fun((list(FSO)) -> {ok, - nil} | - {error, snag:snag()}). -each(Constraint) -> - fun(L) -> _pipe = L, - _pipe@1 = gleam@list:try_map(_pipe, Constraint), - gleam@result:replace(_pipe@1, nil) end. |