-module(glerror). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/glerror.gleam"). -export([inspect_format/1, to_kind/1, string_format/1, to_string/1, pretty_print/1, as_format/1, new/1, error/1, add_context/2, add_context_fn/2, with_source/2, map_kind/2, context/2, lazy_context/2, from_result/3, kind/1]). -export_type([kind_error/1, kind/1, detail/0, format_args/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(" Error handling utilities with support for error kinds, context, and sources.\n"). -opaque kind_error(DPK) :: {kind_error, kind(DPK), list(fun(() -> binary())), list(fun((format_args()) -> binary()))}. -type kind(DPL) :: {kind, DPL, fun((format_args()) -> binary())}. -type detail() :: high | low. -type format_args() :: {format_args, detail()}. -file("src/glerror.gleam", 46). ?DOC(" Formats a term using string.inspect.\n"). -spec inspect_format(any()) -> fun((format_args()) -> binary()). inspect_format(Term) -> fun(_) -> gleam@string:inspect(Term) end. -file("src/glerror.gleam", 29). -spec to_kind(DPQ) -> kind(DPQ). to_kind(Kind) -> {kind, Kind, inspect_format(Kind)}. -file("src/glerror.gleam", 50). -spec string_format(binary()) -> fun((format_args()) -> binary()). string_format(S) -> fun(_) -> S end. -file("src/glerror.gleam", 297). ?DOC( " Convert a KindError to a single-line string representation.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" "\n" " error.error(\"Database connection failed\")\n" " |> error.add_context(\"loading user\")\n" " |> error.add_context(\"fetching profile\")\n" " |> error.to_string\n" " // \"Database connection failed [loading user, fetching profile]\"\n" " ```\n" ). -spec to_string(kind_error(any())) -> binary(). to_string(Err) -> Base = (erlang:element(3, erlang:element(2, Err)))({format_args, low}), With_context = case erlang:element(3, Err) of [] -> Base; Context -> Context_str = begin _pipe = Context, _pipe@1 = lists:reverse(_pipe), _pipe@2 = gleam@list:map(_pipe@1, fun(C) -> C() end), gleam@string:join(_pipe@2, <<", "/utf8>>) end, <<<<<>/binary, Context_str/binary>>/binary, "]"/utf8>> end, case erlang:element(4, Err) of [] -> With_context; Sources -> Sources_str = begin _pipe@3 = Sources, _pipe@4 = gleam@list:map( _pipe@3, fun(S) -> S({format_args, low}) end ), gleam@string:join(_pipe@4, <<", "/utf8>>) end, Sources_title = case erlang:length(Sources) of 1 -> <<"source"/utf8>>; _ -> <<"sources"/utf8>> end, <<<<<<<<<>/binary, Sources_title/binary>>/binary, ": "/utf8>>/binary, Sources_str/binary>>/binary, ")"/utf8>> end. -file("src/glerror.gleam", 407). -spec source_to_doc(fun((format_args()) -> binary()), boolean()) -> glam@doc:document(). source_to_doc(Source, Is_last) -> Connector = case Is_last of true -> <<"╰─▶ "/utf8>>; false -> <<"├─▶ "/utf8>> end, Cont_prefix = case Is_last of true -> <<" "/utf8>>; false -> <<"│ "/utf8>> end, case gleam@string:split(Source({format_args, high}), <<"\n"/utf8>>) of [] -> glam@doc:from_string(Connector); [First | Rest] -> Docs = [glam@doc:from_string(<>) | gleam@list:map( Rest, fun(Line) -> glam@doc:from_string( <> ) end )], _pipe = Docs, _pipe@1 = glam@doc:join(_pipe, {line, 1}), glam@doc:force_break(_pipe@1) end. -file("src/glerror.gleam", 356). ?DOC( " Format a KindError with hierarchical pretty printing.\n" "\n" " This produces a tree-like output with numbered context sections and\n" " box-drawing characters for sources, supporting nested errors via\n" " `as_format`.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" "\n" " error.error(\"Not found\")\n" " |> error.add_context(\"fetching user\")\n" " |> error.add_context(\"loading dashboard\")\n" " |> error.with_source(fn(_) { \"HTTP 404\" })\n" " |> error.pretty_print\n" " ```\n" "\n" " Output:\n" " ```\n" " error: Not found\n" " context:\n" " 0: fetching user\n" " 1: loading dashboard\n" " sources:\n" " ╰─▶ HTTP 404\n" " ```\n" ). -spec pretty_print(kind_error(any())) -> binary(). pretty_print(Err) -> Root_doc = glam@doc:from_string( <<"error: "/utf8, ((erlang:element(3, erlang:element(2, Err)))({format_args, high}))/binary>> ), Parts = [Root_doc], Parts@1 = case erlang:element(3, Err) of [] -> Parts; Context -> Lines = begin _pipe = gleam@list:index_map( Context, fun(C, I) -> glam@doc:from_string( <<<<<<" "/utf8, (erlang:integer_to_binary(I))/binary>>/binary, ": "/utf8>>/binary, (C())/binary>> ) end ), glam@doc:join(_pipe, {line, 1}) end, Context_doc = begin _pipe@1 = [glam@doc:from_string(<<"context:"/utf8>>), Lines], _pipe@2 = glam@doc:join(_pipe@1, {line, 1}), glam@doc:force_break(_pipe@2) end, lists:append(Parts, [Context_doc]) end, Parts@2 = case erlang:element(4, Err) of [] -> Parts@1; Sources -> Source_docs = case Sources of [] -> []; [Single] -> [source_to_doc(Single, true)]; [First | Rest] -> Rest_docs = gleam@list:map( Rest, fun(S) -> source_to_doc(S, true) end ), [source_to_doc(First, false) | Rest_docs] end, Sources_doc = begin _pipe@3 = [glam@doc:from_string(<<"sources:"/utf8>>) | Source_docs], _pipe@4 = glam@doc:join(_pipe@3, {line, 1}), glam@doc:force_break(_pipe@4) end, lists:append(Parts@1, [Sources_doc]) end, _pipe@5 = Parts@2, _pipe@6 = glam@doc:join(_pipe@5, {line, 1}), _pipe@7 = glam@doc:force_break(_pipe@6), glam@doc:to_string(_pipe@7, 80). -file("src/glerror.gleam", 54). -spec as_format(kind_error(any())) -> fun((format_args()) -> binary()). as_format(Error) -> fun(Args) -> case erlang:element(2, Args) of high -> pretty_print(Error); low -> to_string(Error) end end. -file("src/glerror.gleam", 92). ?DOC( " Create a new error from a custom error kind.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" "\n" " pub type MyError {\n" " NotFound\n" " InvalidInput\n" " }\n" "\n" " let err = error.new(error.to_kind(NotFound))\n" " ```\n" ). -spec new(kind(DPV)) -> kind_error(DPV). new(Kind) -> {kind_error, Kind, [], []}. -file("src/glerror.gleam", 104). ?DOC( " Create a simple string-based error.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" " let err = error.error(\"File not found\")\n" " ```\n" ). -spec error(binary()) -> kind_error(binary()). error(Message) -> new({kind, Message, string_format(Message)}). -file("src/glerror.gleam", 123). ?DOC( " Add a context layer to an error.\n" "\n" " Context is added in a stack, with the most recent context appearing first\n" " when the error is displayed.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" " error.error(\"connection refused\")\n" " |> error.add_context(\"connecting to database\")\n" " |> error.add_context(\"initializing application\")\n" " ```\n" ). -spec add_context(kind_error(DPY), binary()) -> kind_error(DPY). add_context(Err, Context) -> {kind_error, erlang:element(2, Err), [fun() -> Context end | erlang:element(3, Err)], erlang:element(4, Err)}. -file("src/glerror.gleam", 127). -spec add_context_fn(kind_error(DQB), fun(() -> binary())) -> kind_error(DQB). add_context_fn(Err, Context) -> {kind_error, erlang:element(2, Err), [Context | erlang:element(3, Err)], erlang:element(4, Err)}. -file("src/glerror.gleam", 155). ?DOC( " Attach a source error description.\n" "\n" " This is useful when you want to include information about the underlying\n" " cause of the error without exposing the actual error type.\n" "\n" " The source is stored as a lazy function and only evaluated when the error\n" " is displayed, avoiding unnecessary string construction.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" " error.error(\"Database query failed\")\n" " |> error.with_source(fn() { \"Connection timeout after 30s\" })\n" "\n" " // With dynamic data\n" " error.error(\"Database query failed\")\n" " |> error.with_source(fn() {\n" " \"Connection timeout at \" <> format_timestamp(now())\n" " })\n" " ```\n" ). -spec with_source(kind_error(DQE), fun((format_args()) -> binary())) -> kind_error(DQE). with_source(Err, Source) -> {kind_error, erlang:element(2, Err), erlang:element(3, Err), lists:append(erlang:element(4, Err), [Source])}. -file("src/glerror.gleam", 181). ?DOC( " Transform the error kind using a mapping function.\n" "\n" " This is useful when converting between different error types.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" "\n" " pub type HttpError {\n" " NotFound\n" " ServerError\n" " }\n" "\n" " pub type AppError {\n" " HttpFailed(HttpError)\n" " ParseFailed\n" " }\n" "\n" " let http_err = error.new(error.to_kind(NotFound))\n" " let app_err = error.map_kind(http_err, fn(e) { error.to_kind(HttpFailed(e)) })\n" " ```\n" ). -spec map_kind(kind_error(DQH), fun((DQH) -> kind(DQJ))) -> kind_error(DQJ). map_kind(Err, Mapper) -> {kind_error, Mapper(erlang:element(2, erlang:element(2, Err))), erlang:element(3, Err), erlang:element(4, Err)}. -file("src/glerror.gleam", 206). ?DOC( " Add context to a Result if it's an error.\n" "\n" " This is the primary way to add context when working with Result types.\n" " The Ok value passes through unchanged.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " parse_json(data)\n" " |> glerror.context(\"parsing user data\")\n" " |> glerror.context(\"loading profile\")\n" " ```\n" ). -spec context({ok, DQM} | {error, kind_error(DQN)}, binary()) -> {ok, DQM} | {error, kind_error(DQN)}. context(Res, Context) -> gleam@result:map_error( Res, fun(_capture) -> add_context(_capture, Context) end ). -file("src/glerror.gleam", 228). ?DOC( " Add context to a Result using a lazy function.\n" "\n" " The function is only called if the Result is an Error, avoiding\n" " unnecessary string construction in the success case.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" "\n" " fetch_user(user_id)\n" " |> error.lazy_context(fn() {\n" " \"fetching user \" <> int.to_string(user_id)\n" " })\n" " ```\n" ). -spec lazy_context({ok, DQU} | {error, kind_error(DQV)}, fun(() -> binary())) -> {ok, DQU} | {error, kind_error(DQV)}. lazy_context(Res, Context) -> case Res of {ok, _} -> Res; {error, Err} -> {error, add_context(Err, Context())} end. -file("src/glerror.gleam", 251). ?DOC( " Convert a Result with a different error type to use KindError.\n" "\n" " This is useful when integrating with libraries that use their own error types.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " int.parse(\"42\")\n" " |> error.from_result(\n" " Kind(\"Invalid integer\", fn(_) { \"Invalid integer\" }),\n" " fn(_) { fn(_) { \"Parse error\" } }\n" " )\n" " ```\n" ). -spec from_result( {ok, DRC} | {error, DRD}, kind(DRG), fun((DRD) -> fun((format_args()) -> binary())) ) -> {ok, DRC} | {error, kind_error(DRG)}. from_result(Res, Kind, Format_source) -> case Res of {ok, Value} -> {ok, Value}; {error, Err} -> {error, begin _pipe = new(Kind), with_source(_pipe, Format_source(Err)) end} end. -file("src/glerror.gleam", 278). ?DOC( " Get the error kind from a KindError.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import glerror as error\n" "\n" " let err = error.new(error.to_kind(NotFound))\n" " error.kind(err) // NotFound\n" " ```\n" ). -spec kind(kind_error(DRL)) -> DRL. kind(Err) -> erlang:element(2, erlang:element(2, Err)).