Skip to content

marianoguerra/einfo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

einfo

A library, macros and parse transform for structured, standard errors with more context

Build

rebar3 compile

Test

rebar3 ct

Use

Add einfo as a dependency on your project, on rebar for example:

{einfo, {git, "https://github.com/marianoguerra/einfo", {branch, "master"}}}

Add the einfo_pt parse transform to your config, on rebar for example:

{erl_opts, [debug_info, {parse_transform, einfo_pt}]}.

For now, on the module you want to use it, include einfo.hrl:

-include_lib("einfo/include/einfo.hrl").

What does it do

it will transform the module in test/pt_samples/module1.erl from:

-module(module1).

    f1 () ->
            A = einfo:error(my_bad),
            f2(A).

    f2(1) -> one;
    f2(2) -> two;
    f2(X) -> einfo:error(badarg, io_lib:format("bad argument: ~p", [X])).

    f3(A, 0) ->
            einfo:error(division_by_zero, "dividing " ++ integer_to_list(A) ++ " by 0");
    f3(A, B) -> A / B.

    f_extra(A) ->
            einfo:error(badarg, io_lib:format("bad argument: ~p", [A]),
                                    #{arg => A, bad => true}).

    wrap() ->
            einfo:wrap(badarg, {error, parent_cause}).

    wrap(Error) ->
            einfo:wrap(badarg, Error).

    wrap_msg() ->
            einfo:wrap(badarg, "Msg", {error, parent_cause}).

    wrap_extra() ->
            einfo:wrap(badarg, "Msg", #{with_parent => true}, {error, parent_cause}).

    format(X) ->
            einfo:format(badarg, "bad argument: ~p", [X]).

    format_extra(A) ->
            einfo:format(badarg, "bad argument: ~p", [A], #{arg => A, bad => true}).

    wrap_format(X) ->
            einfo:wrap_format(badarg, "bad argument: ~p", [X], {error, parent}).

    wrap_format_extra(A) ->
            einfo:wrap_format(badarg, "bad argument: ~p", [A], #{arg => A, bad => true},
             {error, parent}).

to:

-module(module1).

-include_lib("einfo/include/einfo.hrl").

f1() ->
        A = {error,
         #einfo{type = my_bad, msg = "my_bad",
                module = module1, function = f1, arity = 0, line = 8,
                cause = undefined, extra = undefined}},
        f2(A).

f2(1) -> one;
f2(2) -> two;
f2(X) ->
        {error,
         #einfo{type = badarg,
                msg = io_lib:format("bad argument: ~p", [X]),
                module = module1, function = f2, arity = 1, line = 13,
                cause = undefined, extra = undefined}}.

f3(A, 0) ->
        {error,
         #einfo{type = division_by_zero,
                msg = "dividing " ++ integer_to_list(A) ++ " by 0",
                module = module1, function = f3, arity = 2, line = 16,
                cause = undefined, extra = undefined}};
f3(A, B) -> A / B.

f_extra(A) ->
        {error,
         #einfo{type = badarg,
                msg = io_lib:format("bad argument: ~p", [A]),
                module = module1, function = f_extra, arity = 1,
                line = 20, cause = undefined,
                extra = #{arg => A, bad => true}}}.

wrap() ->
        {error,
         #einfo{type = badarg, msg = "badarg",
                module = module1, function = wrap, arity = 0, line = 24,
                cause = {error, parent_cause}, extra = undefined}}.

wrap(Error) ->
        {error,
         #einfo{type = badarg, msg = "badarg",
                module = module1, function = wrap, arity = 1, line = 27,
                cause = Error, extra = undefined}}.

wrap_msg() ->
        {error,
         #einfo{type = badarg, msg = "Msg",
                module = module1, function = wrap_msg, arity = 0,
                line = 30, cause = {error, parent_cause}, extra = undefined}}.

wrap_extra() ->
        {error,
         #einfo{type = badarg, msg = "Msg",
                module = module1, function = wrap_extra, arity = 0,
                line = 33, cause = {error, parent_cause},
                extra = #{with_parent => true}}}.

format(X) ->
        {error,
         #einfo{type = badarg,
                msg = io_lib:format("bad argument: ~p", [X]),
                module = module1, function = format, arity = 1,
                line = 36, cause = undefined, extra = undefined}}.

format_extra(A) ->
        {error,
         #einfo{type = badarg,
                msg = io_lib:format("bad argument: ~p", [A]),
                module = module1, function = format_extra, arity = 1,
                line = 39, cause = undefined,
                extra = #{arg => A, bad => true}}}.

wrap_format(X) ->
        {error,
         #einfo{type = badarg,
                msg = io_lib:format("bad argument: ~p", [X]),
                module = module1, function = wrap_format, arity = 1,
                line = 42, cause = {error, parent}, extra = undefined}}.

wrap_format_extra(A) ->
        {error,
         #einfo{type = badarg,
                msg = io_lib:format("bad argument: ~p", [A]),
                module = module1, function = wrap_format_extra,
                arity = 1, line = 45, cause = {error, parent},
                extra = #{arg => A, bad => true}}}.

Note that include_lib for einfo.hrl will only be included if it wasn't there

API

Parameters

Type
An atom describing the type of error in a computer friendly way What you would put as second element in an error tuple: {error, Type}
Msg
A string describing the error in human a friendly way
Extra
Extra data that serves as context for the error, for example if the error was caused because of a bad key, you can add the key in the extra field
Cause
If this error was caused by another internal error you can put the internal error in this field so you can have traceback-like information
Module
The module where the error was generated as an atom
Function
The function where the error was generated as an atom
Arity
The arity of the function where the error was generated as an int
Line
The line where the error was generated as an int

Parse Transforms

All calls set module, function, arity and line

einfo:error(Type)
Create an error with type set, msg is a string version of type
einfo:error(Type, Msg)
Create an error with type and msg set
einfo:error(Type, Msg, Extra)
Create an error with type and msg and extra set
einfo:wrap(Type, Cause)
Create an error with type and cause set, msg is a string version of type
einfo:wrap(Type, Msg, Cause)
Create an error with type, msg and cause set
einfo:wrap(Type, Msg, Extra, Cause)
Create an error with type, msg, extra and cause set
einfo:format(Type, Format, FormatData)
Create an error with type set, msg is a the result of calling at runtime io_lib:format(Format, FormatData)
einfo:format(Type, Format, FormatData, Extra)
Create an error with type and extra set, msg is a the result of calling at runtime io_lib:format(Format, FormatData)
einfo:wrap_format(Type, Format, FormatData, Cause)
Create an error with type and cause set, msg is a the result of calling at runtime io_lib:format(Format, FormatData)
einfo:wrap_format(Type, Format, FormatData, Extra, Cause)
Create an error with type, extra and cause set, msg is a the result of calling at runtime io_lib:format(Format, FormatData)

Functions

einfo:to_string(EInfo | {error, EInfo})
Return a one line string representation of the error, without the cause Something like: 'Error: {type}@{module}:{function}/{arity}:{line} "{msg}" ({extra})'
einfo:print(EInfo | {error, EInfo})
print string representation with io:format
type(EInfo)
Return the value of the type field
msg(EInfo)
Return the value of the msg field
module(EInfo)
Return the value of the module field
line(EInfo)
Return the value of the line field
function(EInfo)
Return the value of the function field
arity(EInfo)
Return the value of the arity field
cause(EInfo)
Return the value of the cause field
extra(EInfo)
Return the value of the extra field
extra(EInfo, Key)
Lookup Key in the extra field, works if Extra is a Map or PropList, returns undefined if not found
extra(EInfo, Key, Default)
Lookup Key in the extra field, works if Extra is a Map or PropList, returns Default if not found
to_plist(EInfo)
Returns the EInfo record as a proplist
to_map(EInfo)
Returns the EInfo record as a map, supported on Erlang >= 17

Macros

Note: function and arity fields will only be set on Erlang >= 19

NEW_ERROR(Type)
Like einfo:error/1 but as a macro
NEW_ERROR(Type, Msg)
Like einfo:error/2 but as a macro
NEW_ERROR(Type, Msg, Extra)
Like einfo:error/3 but as a macro
WRAP_ERROR(Type, Cause)
Like einfo:wrap/2 but as a macro
WRAP_ERROR(Type, Msg, Cause)
Like einfo:wrap/3 but as a macro
WRAP_ERROR(Type, Msg, Extra, Cause)
Like einfo:wrap/4 but as a macro

Types

-type einfo() :: #einfo{}.
-type type() :: atom().
-type msg() :: string().
-type line() :: non_neg_integer().
-type error() :: {error, einfo()} | {error, atom()} | undefined.
-type extra() :: any().

TODO

  • automatic include_lib doesn't seem to be working

Ideas:

  • maybe include only record definition instead of -include_lib einfo.hrl?

Author

Mariano Guerra

License

BSD, see LICENSE

About

An Erlang library, macros and parse transform for structured, standard errors with more context

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages