-module(outcome). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([defect/1, failure/1, error_with_defect/1, new_stack/1, new_stack_with_defect/1, new_stack_with_failure/1, error_with_failure/1, stack_to_problems/1, unwrap_failure/2, into_defect/1, into_failure/1, map_into_defect/2, as_defect/2, as_failure/2, add_to_stack/2, add_context_to_stack/2, add_context/2, add_defect_to_stack/2, add_failure_to_stack/2, stack_to_lines/1, outcome_to_lines/1, pretty_print/1]). -export_type([problem/0, stack/0]). -type problem() :: {context, binary()} | {defect, binary()} | {failure, binary()}. -type stack() :: {stack, problem(), list(problem())}. -spec defect(binary()) -> problem(). defect(Value) -> {defect, Value}. -spec failure(binary()) -> problem(). failure(Value) -> {failure, Value}. -spec problem_is_error(problem()) -> boolean(). problem_is_error(Problem) -> case Problem of {context, _} -> false; {defect, _} -> true; {failure, _} -> true end. -spec error_with_defect(binary()) -> {ok, any()} | {error, stack()}. error_with_defect(Defect) -> {error, {stack, {defect, Defect}, []}}. -spec new_stack(problem()) -> stack(). new_stack(Error) -> {stack, Error, []}. -spec new_stack_with_defect(binary()) -> stack(). new_stack_with_defect(Failure) -> new_stack({defect, Failure}). -spec new_stack_with_failure(binary()) -> stack(). new_stack_with_failure(Failure) -> new_stack({failure, Failure}). -spec error_with_failure(binary()) -> {ok, any()} | {error, stack()}. error_with_failure(Failure) -> {error, new_stack_with_failure(Failure)}. -spec stack_to_problems(stack()) -> list(problem()). stack_to_problems(Stack) -> [erlang:element(2, Stack) | erlang:element(3, Stack)]. -spec top_problem(list(problem())) -> {ok, problem()} | {error, nil}. top_problem(Problems) -> gleam@list:find(Problems, fun problem_is_error/1). -spec unwrap_failure(stack(), binary()) -> binary(). unwrap_failure(Stack, Default) -> _pipe = Stack, _pipe@1 = stack_to_problems(_pipe), gleam@list:fold_until( _pipe@1, Default, fun(Message, Problem) -> case Problem of {context, _} -> {continue, Message}; {defect, _} -> {stop, Message}; {failure, Failure_message} -> {stop, Failure_message} end end ). -spec into_defect({ok, FWR} | {error, binary()}) -> {ok, FWR} | {error, stack()}. into_defect(Result) -> gleam@result:map_error(Result, fun(E) -> {stack, {defect, E}, []} end). -spec into_failure({ok, FWV} | {error, binary()}) -> {ok, FWV} | {error, stack()}. into_failure(Result) -> gleam@result:map_error(Result, fun(E) -> {stack, {failure, E}, []} end). -spec map_into_defect({ok, FWZ} | {error, FXA}, fun((FXA) -> binary())) -> {ok, FWZ} | {error, stack()}. map_into_defect(Result, Mapper) -> _pipe = Result, _pipe@1 = gleam@result:map_error(_pipe, Mapper), into_defect(_pipe@1). -spec as_defect({ok, FXE} | {error, nil}, binary()) -> {ok, FXE} | {error, stack()}. as_defect(Result, E) -> gleam@result:replace_error(Result, {stack, {defect, E}, []}). -spec as_failure({ok, FXI} | {error, nil}, binary()) -> {ok, FXI} | {error, stack()}. as_failure(Result, E) -> gleam@result:replace_error(Result, {stack, {failure, E}, []}). -spec add_to_stack(stack(), problem()) -> stack(). add_to_stack(Stack, New_problem) -> {stack, New_problem, [erlang:element(2, Stack) | erlang:element(3, Stack)]}. -spec add_context_to_stack(stack(), binary()) -> stack(). add_context_to_stack(Stack, Value) -> add_to_stack(Stack, {context, Value}). -spec add_context({ok, FXM} | {error, stack()}, binary()) -> {ok, FXM} | {error, stack()}. add_context(Outcome, Context) -> gleam@result:map_error( Outcome, fun(Stack) -> add_context_to_stack(Stack, Context) end ). -spec add_defect_to_stack(stack(), binary()) -> stack(). add_defect_to_stack(Stack, Value) -> add_to_stack(Stack, {defect, Value}). -spec add_failure_to_stack(stack(), binary()) -> stack(). add_failure_to_stack(Stack, Value) -> add_to_stack(Stack, {failure, Value}). -spec pretty_print_problem(problem()) -> binary(). pretty_print_problem(Problem) -> case Problem of {context, Value} -> <<"Context: "/utf8, Value/binary>>; {defect, Value@1} -> <<"Defect: "/utf8, Value@1/binary>>; {failure, Value@2} -> <<"Failure: "/utf8, Value@2/binary>> end. -spec stack_to_lines(stack()) -> list(binary()). stack_to_lines(Stack) -> _pipe = stack_to_problems(Stack), gleam@list:map(_pipe, fun pretty_print_problem/1). -spec outcome_to_lines({ok, any()} | {error, stack()}) -> list(binary()). outcome_to_lines(Outcome) -> case Outcome of {ok, _} -> []; {error, Stack} -> stack_to_lines(Stack) end. -spec pretty_print(stack()) -> binary(). pretty_print(Stack) -> Problem = begin _pipe = Stack, _pipe@1 = stack_to_problems(_pipe), _pipe@2 = top_problem(_pipe@1), _pipe@3 = gleam@result:map(_pipe@2, fun pretty_print_problem/1), gleam@result:unwrap(_pipe@3, <<"Error"/utf8>>) end, Stack@1 = begin _pipe@4 = stack_to_lines(Stack), gleam@string:join(_pipe@4, <<"\n "/utf8>>) end, <<<>/binary, Stack@1/binary>>.