-module(cmd@run). -compile(no_auto_import). -export([run_command/1, run_all_command/1]). -export_type([solve_err/0, err/0, direction/0, gleam_err/0]). -type solve_err() :: undef | {run_failed, binary()}. -type err() :: {failed_to_read_input, binary()} | {unregistered, integer()} | {other, binary()}. -type direction() :: both. -type gleam_err() :: {gleam_err, gleam@erlang@atom:atom_(), binary(), binary(), integer(), binary(), gleam@option:option(gleam@dynamic:dynamic())}. -spec err_to_snag(err()) -> snag:snag(). err_to_snag(Err) -> _pipe = case Err of {unregistered, Day} -> <<<<<<<<"day"/utf8, " "/utf8>>/binary, (gleam@int:to_string(Day))/binary>>/binary, " "/utf8>>/binary, "unregistered"/utf8>>; {failed_to_read_input, Input_path} -> <<"failed to read input file: "/utf8, Input_path/binary>>; {other, S} -> S end, snag:new(_pipe). -spec string_trim(binary(), direction(), binary()) -> binary(). string_trim(S, Dir, Sub) -> string:trim(S, Dir, erlang:binary_to_list(Sub)). -spec do( integer(), gleam@map:map_(integer(), {fun((binary()) -> gleam@dynamic:dynamic()), fun((binary()) -> gleam@dynamic:dynamic())}), boolean() ) -> {ok, {{ok, gleam@dynamic:dynamic()} | {error, solve_err()}, {ok, gleam@dynamic:dynamic()} | {error, solve_err()}}} | {error, err()}. do(Day, Runners, Allow_crash) -> case begin _pipe = gleam@map:get(Runners, Day), gleam@result:replace_error(_pipe, {unregistered, Day}) end of {error, _try} -> {error, _try}; {ok, {Pt_1, Pt_2}} -> Input_path = gleam@string:join( [<<"input/day_"/utf8>>, gleam@int:to_string(Day), <<".txt"/utf8>>], <<""/utf8>> ), case begin _pipe@1 = Input_path, _pipe@2 = gleam@erlang@file:read(_pipe@1), _pipe@3 = gleam@result:map( _pipe@2, fun(_capture) -> string_trim(_capture, both, <<"\n"/utf8>>) end ), gleam@result:replace_error( _pipe@3, {failed_to_read_input, Input_path} ) end of {error, _try@1} -> {error, _try@1}; {ok, Input} -> case Allow_crash of true -> {ok, {{ok, Pt_1(Input)}, {ok, Pt_2(Input)}}}; false -> Pt_1@1 = begin _pipe@4 = fun() -> Pt_1(Input) end, _pipe@5 = gleam_erlang_ffi:rescue(_pipe@4), gleam@result:map_error( _pipe@5, fun run_err_to_string/1 ) end, Pt_2@1 = begin _pipe@6 = fun() -> Pt_2(Input) end, _pipe@7 = gleam_erlang_ffi:rescue(_pipe@6), gleam@result:map_error( _pipe@7, fun run_err_to_string/1 ) end, {ok, {Pt_1@1, Pt_2@1}} end end end. -spec crash_to_dyn(gleam@erlang:crash()) -> gleam@dynamic:dynamic(). crash_to_dyn(Err) -> case Err of {errored, Dyn} -> Dyn; {exited, Dyn} -> Dyn; {thrown, Dyn} -> Dyn end. -spec decode_gleam_err() -> fun((gleam@dynamic:dynamic()) -> {ok, gleam_err()} | {error, list(gleam@dynamic:decode_error())}). decode_gleam_err() -> gleam@dynamic:decode6( fun(Field@0, Field@1, Field@2, Field@3, Field@4, Field@5) -> {gleam_err, Field@0, Field@1, Field@2, Field@3, Field@4, Field@5} end, gleam@dynamic:field( erlang:binary_to_atom(<<"gleam_error"/utf8>>), fun gleam@erlang@atom:from_dynamic/1 ), gleam@dynamic:field( erlang:binary_to_atom(<<"module"/utf8>>), fun gleam@dynamic:string/1 ), gleam@dynamic:field( erlang:binary_to_atom(<<"function"/utf8>>), fun gleam@dynamic:string/1 ), gleam@dynamic:field( erlang:binary_to_atom(<<"line"/utf8>>), fun gleam@dynamic:int/1 ), gleam@dynamic:field( erlang:binary_to_atom(<<"message"/utf8>>), fun gleam@dynamic:string/1 ), gleam@dynamic:any( [gleam@dynamic:field( erlang:binary_to_atom(<<"value"/utf8>>), gleam@dynamic:optional(fun gleam@dynamic:dynamic/1) ), fun(_) -> {ok, none} end] ) ). -spec gleam_err_to_string(gleam_err()) -> binary(). gleam_err_to_string(G) -> gleam@string:join( [<<"error:"/utf8>>, erlang:atom_to_binary(erlang:element(2, G)), <<"-"/utf8>>, erlang:element(6, G), <<"in module"/utf8>>, erlang:element(3, G), <<"in function"/utf8>>, erlang:element(4, G), <<"at line"/utf8>>, gleam@int:to_string(erlang:element(5, G)), begin _pipe = erlang:element(7, G), _pipe@1 = gleam@option:map( _pipe, fun(Val) -> <<"with value "/utf8, (gleam@string:inspect(Val))/binary>> end ), gleam@option:unwrap(_pipe@1, <<""/utf8>>) end], <<" "/utf8>> ). -spec run_err_to_string(gleam@erlang:crash()) -> solve_err(). run_err_to_string(Err) -> Dyn = crash_to_dyn(Err), _pipe = (decode_gleam_err())(Dyn), _pipe@1 = gleam@result:map(_pipe, fun gleam_err_to_string/1), _pipe@2 = gleam@result:lazy_unwrap( _pipe@1, fun() -> <<"run failed for some reason: "/utf8, (gleam@string:inspect(Err))/binary>> end ), {run_failed, _pipe@2}. -spec run_res_to_string({ok, gleam@dynamic:dynamic()} | {error, solve_err()}) -> binary(). run_res_to_string(Res) -> case Res of {ok, Res@1} -> gleam@string:inspect(Res@1); {error, Err} -> case Err of undef -> <<"function undefined"/utf8>>; {run_failed, S} -> S end end. -spec collect( {integer(), {ok, {{ok, gleam@dynamic:dynamic()} | {error, solve_err()}, {ok, gleam@dynamic:dynamic()} | {error, solve_err()}}} | {error, err()}} ) -> binary(). collect(X) -> Day = gleam@int:to_string(erlang:element(1, X)), case erlang:element(2, X) of {ok, {Res_1, Res_2}} -> <<<<<<<<<<<<<<"Ran day "/utf8, Day/binary>>/binary, ":\n"/utf8>>/binary, " Part 1: "/utf8>>/binary, (run_res_to_string(Res_1))/binary>>/binary, "\n"/utf8>>/binary, " Part 2: "/utf8>>/binary, (run_res_to_string(Res_2))/binary>>; {error, Err} -> _pipe = Err, _pipe@1 = err_to_snag(_pipe), _pipe@2 = snag:layer( _pipe@1, gleam@string:append(<<"failed to run day "/utf8>>, Day) ), snag:pretty_print(_pipe@2) end. -spec timeout_flag() -> {binary(), glint@flag:contents()}. timeout_flag() -> glint@flag:int(<<"timeout"/utf8>>, 0, <<"Run with specified timeout"/utf8>>). -spec allow_crash_flag() -> {binary(), glint@flag:contents()}. allow_crash_flag() -> glint@flag:bool( <<"allow-crash"/utf8>>, false, <<"Don't catch exceptions thrown by runners"/utf8>> ). -spec run_command( gleam@map:map_(integer(), {fun((binary()) -> gleam@dynamic:dynamic()), fun((binary()) -> gleam@dynamic:dynamic())}) ) -> glint:stub({ok, list(binary())} | {error, snag:snag()}). run_command(Runners) -> {stub, [<<"run"/utf8>>], fun(_capture) -> run(_capture, Runners, false) end, [timeout_flag(), allow_crash_flag()], <<"Run the specified days"/utf8>>}. -spec run_all_command( gleam@map:map_(integer(), {fun((binary()) -> gleam@dynamic:dynamic()), fun((binary()) -> gleam@dynamic:dynamic())}) ) -> glint:stub({ok, list(binary())} | {error, snag:snag()}). run_all_command(Runners) -> {stub, [<<"run"/utf8>>, <<"all"/utf8>>], fun(_capture) -> run(_capture, Runners, true) end, [timeout_flag(), allow_crash_flag()], <<"Run all registered days"/utf8>>}. -spec run( glint:command_input(), gleam@map:map_(integer(), {fun((binary()) -> gleam@dynamic:dynamic()), fun((binary()) -> gleam@dynamic:dynamic())}), boolean() ) -> {ok, list(binary())} | {error, snag:snag()}. run(Input, Runners, Run_all) -> {ok, {i, Timeout@1}} = case glint@flag:get( erlang:element(3, Input), erlang:element(1, timeout_flag()) ) of {ok, {i, Timeout}} -> {ok, {i, Timeout}}; _try -> erlang:error(#{gleam_error => assert, message => <<"Assertion pattern match failed"/utf8>>, value => _try, module => <<"cmd/run"/utf8>>, function => <<"run"/utf8>>, line => 216}) end, {ok, {b, Allow_crash@1}} = case glint@flag:get( erlang:element(3, Input), erlang:element(1, allow_crash_flag()) ) of {ok, {b, Allow_crash}} -> {ok, {b, Allow_crash}}; _try@1 -> erlang:error(#{gleam_error => assert, message => <<"Assertion pattern match failed"/utf8>>, value => _try@1, module => <<"cmd/run"/utf8>>, function => <<"run"/utf8>>, line => 217}) end, case case Timeout@1 of 0 -> {ok, endless}; _@1 when Timeout@1 < 0 -> invalid_timeout_err(Timeout@1); _@2 -> {ok, {ending, Timeout@1}} end of {error, _try@2} -> {error, _try@2}; {ok, Timing} -> case case Run_all of true -> _pipe = Runners, _pipe@1 = gleam@map:keys(_pipe), _pipe@2 = gleam@list:sort(_pipe@1, fun gleam@int:compare/2), {ok, _pipe@2}; false -> _pipe@3 = erlang:element(2, Input), _pipe@4 = parse:days(_pipe@3), wrap_failed_to_parse_err(_pipe@4, erlang:element(2, Input)) end of {error, _try@3} -> {error, _try@3}; {ok, Days} -> _pipe@5 = Days, _pipe@6 = cmd:exec( _pipe@5, Timing, fun(_capture) -> do(_capture, Runners, Allow_crash@1) end, fun(Field@0) -> {other, Field@0} end, fun collect/1 ), {ok, _pipe@6} end end. -spec invalid_timeout_err(integer()) -> {ok, any()} | {error, snag:snag()}. invalid_timeout_err(Timeout) -> _pipe = [<<"invalid timeout value "/utf8>>, <<"'"/utf8>>, gleam@int:to_string(Timeout), <<"'"/utf8>>], _pipe@1 = gleam@string:concat(_pipe), _pipe@2 = snag:error(_pipe@1), snag:context( _pipe@2, <<"timeout must be greater than or equal to 1 ms"/utf8>> ). -spec wrap_failed_to_parse_err({ok, HIR} | {error, snag:snag()}, list(binary())) -> {ok, HIR} | {error, snag:snag()}. wrap_failed_to_parse_err(Res, Args) -> snag:context( Res, gleam@string:join( [<<"failed to parse arguments:"/utf8>> | Args], <<" "/utf8>> ) ).