From 5ae69f12c9861d48ea6caa71f25e9a83337a6e15 Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Sat, 11 Sep 2021 20:06:24 +0100 Subject: [PATCH] Remove logger functions for now --- CHANGELOG.md | 5 + rebar.config | 2 +- rebar.lock | 6 +- src/gleam/erlang.gleam | 192 ++++++-------------------- src/gleam/erlang/atom.gleam | 6 +- src/gleam/erlang/logger.gleam | 112 --------------- src/gleam/erlang/logger/handler.gleam | 71 ---------- src/gleam_erlang.app.src | 2 +- src/gleam_erlang_ffi.erl | 14 +- test/gleam/erlang/atom_test.gleam | 90 ++++++------ test/gleam/erlang/charlist_test.gleam | 29 ++-- test/gleam/erlang_test.gleam | 25 ++-- 12 files changed, 132 insertions(+), 422 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 src/gleam/erlang/logger.gleam delete mode 100644 src/gleam/erlang/logger/handler.gleam diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..16a37ec --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## v0.1.0 - 2021-09-11 + +- Initial release diff --git a/rebar.config b/rebar.config index 47b3d14..4e12f9e 100644 --- a/rebar.config +++ b/rebar.config @@ -8,5 +8,5 @@ {project_plugins, [rebar_gleam, rebar3_hex]}. {deps, [ - {gleam_stdlib, "0.16.0"} + {gleam_stdlib, "~> 0.17.0"} ]}. diff --git a/rebar.lock b/rebar.lock index dfab758..45503cb 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,8 +1,8 @@ {"1.2.0", -[{<<"gleam_stdlib">>,{pkg,<<"gleam_stdlib">>,<<"0.16.0">>},0}]}. +[{<<"gleam_stdlib">>,{pkg,<<"gleam_stdlib">>,<<"0.17.0">>},0}]}. [ {pkg_hash,[ - {<<"gleam_stdlib">>, <<"A4D856EF2AA0503E82CBC9E5E75F466B6D607A3332C6590E39B161B2245E23C5">>}]}, + {<<"gleam_stdlib">>, <<"186494B596DB67A1FDAFFF164F988737AE47A5C634893422397A356CA3C3F979">>}]}, {pkg_hash_ext,[ - {<<"gleam_stdlib">>, <<"288EC0C4B80AF55CADDF09A890B5800BF12227218C5D17014F8B73D213ACFD00">>}]} + {<<"gleam_stdlib">>, <<"66834E96F3F42609B4E5AAA2B73F9EC45625A422D0AED091B3A704ACAE0420E7">>}]} ]. diff --git a/src/gleam/erlang.gleam b/src/gleam/erlang.gleam index 3d500e2..e295fa5 100644 --- a/src/gleam/erlang.gleam +++ b/src/gleam/erlang.gleam @@ -1,8 +1,5 @@ -import gleam/erlang/atom.{Atom} import gleam/dynamic.{Dynamic} -import gleam/function.{rescue} import gleam/int -import gleam/io import gleam/list import gleam/map import gleam/result @@ -13,7 +10,7 @@ external fn erl_format(String, List(a)) -> Charlist = "io_lib" "format" /// Return a string representation of any term -pub fn format(term) { +pub fn format(term: any) -> String { charlist.to_string(erl_format("~p", [term])) } @@ -27,166 +24,20 @@ type Safe { external fn erl_binary_to_term(BitString, List(Safe)) -> Dynamic = "erlang" "binary_to_term" -pub fn binary_to_term(binary) { +pub fn binary_to_term(binary: BitString) -> Result(Dynamic, Nil) { case rescue(fn() { erl_binary_to_term(binary, [Safe]) }) { Ok(term) -> Ok(term) Error(_) -> Error(Nil) } } -pub fn unsafe_binary_to_term(binary) { +pub fn unsafe_binary_to_term(binary: BitString) -> Result(Dynamic, Nil) { case rescue(fn() { erl_binary_to_term(binary, []) }) { Ok(term) -> Ok(term) Error(_) -> Error(Nil) } } -/// All reasons an that an erlang process might by the runtime. -/// -/// http://erlang.org/documentation/doc-9.3/doc/reference_manual/errors.html#exit_reasons -/// Note `erlang:exit` and `erlang:error` can be called with any term. -/// Therefore when captureing errors always make sure to safely cast to this type, i.e. using `cast_exit_reason` -pub type ExitReason { - Badarg - Badarith - Badmatch(Dynamic) - FunctionClause - CaseClause(Dynamic) - IfClause - TryClause(Dynamic) - Undef - Badfun(Dynamic) - Badarity(Dynamic) - TimeoutValue - Noproc - Nocatch(Dynamic) - SystemLimit -} - -/// A stacktrace data structure -pub type Stacktrace = - List(#(Atom, String, Int, String, Int)) - -/// Safely transform dynamic to exit reason type -pub fn cast_exit_reason(raw) { - let badarg = dynamic.from(atom.create_from_string("badarg")) - let badarith = dynamic.from(atom.create_from_string("badarith")) - let badmatch = dynamic.from(atom.create_from_string("badmatch")) - let function_clause = dynamic.from(atom.create_from_string("function_clause")) - let case_clause = dynamic.from(atom.create_from_string("case_clause")) - let if_clause = dynamic.from(atom.create_from_string("if_clause")) - let try_clause = dynamic.from(atom.create_from_string("try_clause")) - let undef = dynamic.from(atom.create_from_string("undef")) - let badfun = dynamic.from(atom.create_from_string("badfun")) - let badarity = dynamic.from(atom.create_from_string("badarity")) - let timeout_value = dynamic.from(atom.create_from_string("timeout_value")) - let noproc = dynamic.from(atom.create_from_string("noproc")) - let nocatch = dynamic.from(atom.create_from_string("nocatch")) - let system_limit = dynamic.from(atom.create_from_string("system_limit")) - - let key = - dynamic.element(raw, 0) - |> result.unwrap(raw) - case key, dynamic.element(raw, 1) { - k, Error(_) if k == badarg -> Ok(Badarg) - k, Error(_) if k == badarith -> Ok(Badarith) - k, Ok(term) if k == badmatch -> Ok(Badmatch(term)) - k, Error(_) if k == function_clause -> Ok(FunctionClause) - k, Ok(term) if k == case_clause -> Ok(CaseClause(term)) - k, Error(_) if k == if_clause -> Ok(IfClause) - k, Ok(term) if k == try_clause -> Ok(TryClause(term)) - k, Error(_) if k == undef -> Ok(Undef) - k, Ok(term) if k == badfun -> Ok(Badfun(term)) - k, Ok(term) if k == badarity -> Ok(Badarity(term)) - k, Error(_) if k == timeout_value -> Ok(TimeoutValue) - k, Error(_) if k == noproc -> Ok(Noproc) - k, Ok(term) if k == nocatch -> Ok(Nocatch(term)) - k, Error(_) if k == system_limit -> Ok(SystemLimit) - _, _ -> Error("Not a runtime exit reason") - } -} - -pub type Location { - Location(module: String, function: String, line: Int) -} - -pub type DevReason { - Todo(location: Location, message: String) - AssertNotMatched(location: Location, value: Dynamic) -} - -pub fn cast_dev_reason(raw) { - try gleam_error = dynamic.field(raw, atom.create_from_string("gleam_error")) - try gleam_error = atom.from_dynamic(gleam_error) - try module = dynamic.field(raw, atom.create_from_string("module")) - try module = dynamic.string(module) - try function = dynamic.field(raw, atom.create_from_string("function")) - try function = dynamic.string(function) - try line = dynamic.field(raw, atom.create_from_string("line")) - try line = dynamic.int(line) - let location = Location(module, function, line) - - let todo_atom = atom.create_from_string("todo") - let assert_atom = atom.create_from_string("assert") - - let message = - dynamic.field(raw, atom.create_from_string("message")) - |> result.then(dynamic.string) - let value = dynamic.field(raw, atom.create_from_string("value")) - case gleam_error, message, value { - e, Ok(message), _ if e == todo_atom -> Todo(location, message) - e, _, Ok(value) if e == assert_atom -> AssertNotMatched(location, value) - } - |> Ok -} - -/// Safely cast a dynamic stacktrace to typed data. -pub fn cast_stacktrace(raw) -> Result(Stacktrace, String) { - try raw_frames = dynamic.list(raw) - list.try_map(raw_frames, cast_stack_frame) -} - -// https://github.com/elixir-lang/elixir/blob/76d245b6081c53228bf99fc1494add5de7872065/lib/elixir/lib/exception.ex#L28 -// stacktrace is module, function (atom or charlist), args_or_arity, location(keyword list) -fn cast_stack_frame(raw) { - try module = - dynamic.element(raw, 0) - |> result.then(atom.from_dynamic) - - try function_raw = dynamic.element(raw, 1) - let function = case atom.from_dynamic(function_raw) { - Ok(function) -> atom.to_string(function) - Error(_) -> charlist.to_string(dynamic.unsafe_coerce(function_raw)) - } - - try arity_raw = dynamic.element(raw, 2) - let arity = case dynamic.int(arity_raw) { - Ok(arity) -> arity - Error(_) -> list.length(dynamic.unsafe_coerce(arity_raw)) - } - - try location = - dynamic.element(raw, 3) - |> result.then(dynamic.typed_list(_, dynamic.tuple2)) - |> result.map(map.from_list) - - let file_atom = dynamic.from(atom.create_from_string("file")) - let line_atom = dynamic.from(atom.create_from_string("line")) - - try filename = - map.get(location, file_atom) - |> result.map_error(fn(_: Nil) { "Missing key 'file'" }) - - let filename = charlist.to_string(dynamic.unsafe_coerce(filename)) - - try line_number = - map.get(location, line_atom) - |> result.map_error(fn(_: Nil) { "Missing key 'line'" }) - |> result.then(dynamic.int) - - Ok(#(module, function, arity, filename, line_number)) -} - /// Error value returned by `get_line` function /// pub type GetLineError { @@ -198,9 +49,44 @@ pub type GetLineError { /// /// # Example /// -/// > io.get_line("Language: ") +/// > get_line("Language: ") /// // -> Language: <- gleam /// Ok("gleam\n") /// pub external fn get_line(prompt: String) -> Result(String, GetLineError) = "gleam_erlang_ffi" "get_line" + +pub type TimeUnit { + Second + Millisecond + Microsecond + Nanosecond +} + +/// Returns the current OS system time. +/// +/// https://erlang.org/doc/apps/erts/time_correction.html#OS_System_Time +pub external fn system_time(TimeUnit) -> Int = + "os" "system_time" + +/// Returns the current OS system time as a tuple of Ints +/// +/// http://erlang.org/doc/man/os.html#timestamp-0 +pub external fn erlang_timestamp() -> #(Int, Int, Int) = + "os" "timestamp" + +/// Gleam doesn't offer any way to raise exceptions, but they may still occur +/// due to bugs when working with unsafe code, such as when calling Erlang +/// function. +/// +/// This function will catch any error thrown and convert it into a result +/// rather than crashing the process. +/// +pub external fn rescue(fn() -> a) -> Result(a, Crash) = + "gleam_erlang_ffi" "rescue" + +pub type Crash { + Exited(Dynamic) + Thrown(Dynamic) + Errored(Dynamic) +} diff --git a/src/gleam/erlang/atom.gleam b/src/gleam/erlang/atom.gleam index c57d4b1..79e3c58 100644 --- a/src/gleam/erlang/atom.gleam +++ b/src/gleam/erlang/atom.gleam @@ -1,4 +1,4 @@ -import gleam/dynamic.{Dynamic} +import gleam/dynamic.{DecodeError, Dynamic} /// Atom is a special string-like data-type that is most commonly used for /// interfacing with code written in other BEAM languages such as Erlang and @@ -73,7 +73,7 @@ pub external fn to_string(Atom) -> String = /// OK("hello") /// /// > atom(from(123)) -/// Error("Expected an Atom, got `123`") +/// Error(DecodeError(expected: "Int", found: "Int")) /// -pub external fn from_dynamic(from: Dynamic) -> Result(Atom, String) = +pub external fn from_dynamic(from: Dynamic) -> Result(Atom, DecodeError) = "gleam_erlang_ffi" "atom_from_dynamic" diff --git a/src/gleam/erlang/logger.gleam b/src/gleam/erlang/logger.gleam deleted file mode 100644 index 6a8af5a..0000000 --- a/src/gleam/erlang/logger.gleam +++ /dev/null @@ -1,112 +0,0 @@ -// TODO: test this module. -// Default setting is false for SASL compatible https://erlang.org/doc/man/kernel_app.html#logger_sasl_compatible -import gleam -import gleam/erlang/atom.{Atom} -import gleam/dynamic.{Dynamic} -import gleam/map.{Map} -import gleam/result -import gleam/io -import gleam/erlang.{Stacktrace} - -// These are the log levels used in the erlang logger -// https://erlang.org/doc/apps/kernel/logger_chapter.html#log-level -// They match with a set defined in the Syslog Protocol, RFC 5424 -pub type Level { - Emergency - Alert - Critical - Error - Warning - Notice - Info - Debug -} - -pub fn log_level_from_dynamic(raw: Dynamic) { - let emergency = dynamic.from(Emergency) - let alert = dynamic.from(Alert) - let critical = dynamic.from(Critical) - let error = dynamic.from(Error) - let warning = dynamic.from(Warning) - let notice = dynamic.from(Notice) - let info = dynamic.from(Info) - let debug = dynamic.from(Debug) - - case raw { - a if a == emergency -> Ok(Emergency) - a if a == alert -> Ok(Alert) - a if a == critical -> Ok(Critical) - a if a == error -> Ok(Error) - a if a == warning -> Ok(Warning) - a if a == notice -> Ok(Notice) - a if a == info -> Ok(Info) - a if a == debug -> Ok(Debug) - _ -> gleam.Error("Not a valid logger level") - } -} - -// Erl log event is this or string or format plus args -pub type Event { - Event(level: Level, meta: Map(Atom, Dynamic), message: Message) -} - -// https://erlang.org/doc/man/logger.html#HModule:log-2 -// https://erlang.org/doc/man/logger.html#type-log_event -pub fn cast_log_event(raw) { - try level = dynamic.field(raw, atom.create_from_string("level")) - try level = log_level_from_dynamic(level) - - try meta = dynamic.field(raw, atom.create_from_string("meta")) - // Documentation says this field must be in format Map(Atom, term) - let metadata: Map(Atom, Dynamic) = dynamic.unsafe_coerce(meta) - - try msg = dynamic.field(raw, atom.create_from_string("msg")) - try message = log_message_from_dynamic(msg) - Event(level, metadata, message) - |> Ok -} - -pub type Message { - Format(string: String, args: List(Dynamic)) - Report(Dynamic) - String(String) -} - -pub fn log_message_from_dynamic(raw: Dynamic) { - try first = dynamic.element(raw, 0) - try second = dynamic.element(raw, 1) - - let report = atom.create_from_string("report") - let string = atom.create_from_string("string") - - case atom.from_dynamic(first) { - Ok(k) if k == report -> Ok(Report(second)) - Ok(k) if k == string -> { - try str = dynamic.string(second) - Ok(String(str)) - } - _ -> { - try format_str = dynamic.string(first) - try args = dynamic.list(second) - Ok(Format(format_str, args)) - } - } -} - -external fn erl_add_handler(Atom, Atom, Map(Atom, Dynamic)) -> Dynamic = - "logger" "add_handler" - -pub fn add_handler(handler: fn(Dynamic, Stacktrace, Int) -> Nil) -> Nil { - let handler_module = atom.create_from_string("gleam@erlang@logger@handler") - let config = - map.from_list([ - #(atom.create_from_string("handler"), dynamic.from(handler)), - #( - atom.create_from_string("level"), - dynamic.from(atom.create_from_string("error")), - ), - ]) - erl_add_handler(handler_module, handler_module, config) - |> io.debug - Nil -} diff --git a/src/gleam/erlang/logger/handler.gleam b/src/gleam/erlang/logger/handler.gleam deleted file mode 100644 index 9581eec..0000000 --- a/src/gleam/erlang/logger/handler.gleam +++ /dev/null @@ -1,71 +0,0 @@ -import gleam/erlang/atom -import gleam/dynamic -import gleam/list -import gleam/map -import gleam/result -import gleam/int -import gleam/io -import gleam/os -import gleam/string -import gleam/erlang -import gleam/erlang/logger.{Event, Report} - -// TODO what happens if exit normal -fn do_log(event, config) { - try Event(_level, metadata, message) = logger.cast_log_event(event) - assert Ok(handler) = map.get(config, atom.create_from_string("handler")) - let timestamp = - map.get(metadata, atom.create_from_string("time")) - |> result.map_error(fn(_) { "time should be an integer" }) - |> result.then(dynamic.int) - // timestamp is always added by logger to metadata - // Include system_time call as fallback - |> result.lazy_unwrap(fn() { os.system_time(os.Microsecond) }) - - let timestamp = timestamp / 1000000 - - case message { - Report(report) -> { - try #(reason, stacktrace) = cast_proc_lib_report(report) - handler(reason, stacktrace, timestamp) - Ok(Nil) - } - _ -> Ok(Nil) - } -} - -pub fn log(event, config) { - // https://github.com/gleam-lang/gleam/issues/1183 - // assert Ok(_) = do_log(event, config) - case do_log(event, config) { - Ok(_) -> Nil - } -} - -pub fn cast_proc_lib_report(raw) { - try report = dynamic.field(raw, atom.create_from_string("report")) - try [report, _linked] = dynamic.list(report) - try report = dynamic.typed_list(report, dynamic.tuple2) - try error_info = - list.key_find(report, dynamic.from(atom.create_from_string("error_info"))) - |> result.map_error(fn(_) { "Missing error_info key" }) - - try kind = dynamic.element(error_info, 0) - try kind = atom.from_dynamic(kind) - - let error = atom.create_from_string("error") - // Other kinds are catch and exit. - // Exit is same as error, with no stacktrace - // catch should be an error with no catch - case kind { - k if k == error -> { - try reason = dynamic.element(error_info, 1) - // io.debug(reason) - // try reason = erlang.cast_exit_reason(reason) - try stacktrace = dynamic.element(error_info, 2) - try stacktrace = erlang.cast_stacktrace(stacktrace) - #(reason, stacktrace) - |> Ok - } - } -} diff --git a/src/gleam_erlang.app.src b/src/gleam_erlang.app.src index 822c41b..eb6ac1d 100644 --- a/src/gleam_erlang.app.src +++ b/src/gleam_erlang.app.src @@ -1,5 +1,5 @@ {application, gleam_erlang, - [{description, "A Gleam library for Erlang specific code"}, + [{description, "A Gleam library for working with Erlang"}, {vsn, "0.1.0"}, {registered, []}, {applications, diff --git a/src/gleam_erlang_ffi.erl b/src/gleam_erlang_ffi.erl index f5be089..6e7c1ab 100644 --- a/src/gleam_erlang_ffi.erl +++ b/src/gleam_erlang_ffi.erl @@ -1,6 +1,6 @@ -module(gleam_erlang_ffi). -export([atom_from_dynamic/1, atom_create_from_string/1, atom_to_string/1, - atom_from_string/1, get_line/1]). + rescue/1, atom_from_string/1, get_line/1]). -spec atom_from_string(binary()) -> {ok, atom()} | {error, atom_not_loaded}. atom_from_string(S) -> @@ -20,8 +20,8 @@ atom_to_string(S) -> -spec atom_from_dynamic(any()) -> {ok, atom()} | {error, binary()}. atom_from_dynamic(Data) when is_atom(Data) -> {ok, Data}; -atom_from_dynamic(_) -> - {error, list_to_binary("expected an atom, got some other type")}. +atom_from_dynamic(Data) -> + {error, {decode_error, <<"Atom">>, gleam@dynamic:classify(Data)}}. -spec get_line(io:prompt()) -> {ok, unicode:unicode_binary()} | {error, eof | no_data}. get_line(Prompt) -> @@ -31,3 +31,11 @@ get_line(Prompt) -> Data when is_binary(Data) -> {ok, Data}; Data when is_list(Data) -> {ok, unicode:characters_to_binary(Data)} end. + +rescue(F) -> + try {ok, F()} + catch + throw:X -> {error, {thrown, X}}; + error:X -> {error, {errored, X}}; + exit:X -> {error, {exited, X}} + end. diff --git a/test/gleam/erlang/atom_test.gleam b/test/gleam/erlang/atom_test.gleam index 479204d..13f28e5 100644 --- a/test/gleam/erlang/atom_test.gleam +++ b/test/gleam/erlang/atom_test.gleam @@ -1,70 +1,64 @@ import gleam/erlang/atom -import gleam/dynamic -import gleam/should +import gleam/dynamic.{DecodeError} pub fn from_string_test() { atom.create_from_string("this is an existing atom") - "this is an existing atom" - |> atom.from_string - |> should.be_ok + assert Ok(_) = atom.from_string("this is an existing atom") - "this is not an atom we have seen before" - |> atom.from_string - |> should.equal(Error(atom.AtomNotLoaded)) + assert Error(atom.AtomNotLoaded) = + atom.from_string("this is not an atom we have seen before") } pub fn create_from_string_test() { - "ok" - |> atom.create_from_string - |> Ok - |> should.equal(atom.from_string("ok")) + let result = + "ok" + |> atom.create_from_string + |> Ok + assert True = result == atom.from_string("ok") - "expect" - |> atom.create_from_string - |> Ok - |> should.equal(atom.from_string("expect")) + let result = + "expect" + |> atom.create_from_string + |> Ok + assert True = result == atom.from_string("expect") - "this is another atom we have not seen before" - |> atom.create_from_string - |> Ok - |> should.equal(atom.from_string( - "this is another atom we have not seen before", - )) + let result = + "this is another atom we have not seen before" + |> atom.create_from_string + |> Ok + assert True = + result == atom.from_string("this is another atom we have not seen before") } pub fn to_string_test() { - "ok" - |> atom.create_from_string - |> atom.to_string - |> should.equal("ok") + assert "ok" = atom.to_string(atom.create_from_string("ok")) - "expect" - |> atom.create_from_string - |> atom.to_string - |> should.equal("expect") + assert "expect" = atom.to_string(atom.create_from_string("expect")) } pub fn from_dynamic_test() { - "" - |> atom.create_from_string - |> dynamic.from - |> atom.from_dynamic - |> should.equal(Ok(atom.create_from_string(""))) + let result = + "" + |> atom.create_from_string + |> dynamic.from + |> atom.from_dynamic + assert True = result == Ok(atom.create_from_string("")) - "ok" - |> atom.create_from_string - |> dynamic.from - |> atom.from_dynamic - |> should.equal(Ok(atom.create_from_string("ok"))) + let result = + "ok" + |> atom.create_from_string + |> dynamic.from + |> atom.from_dynamic + assert True = result == Ok(atom.create_from_string("ok")) - 1 - |> dynamic.from - |> atom.from_dynamic - |> should.be_error + assert Error(DecodeError(expected: "Atom", found: "Int")) = + 1 + |> dynamic.from + |> atom.from_dynamic - [] - |> dynamic.from - |> atom.from_dynamic - |> should.be_error + assert Error(DecodeError(expected: "Atom", found: "List")) = + [] + |> dynamic.from + |> atom.from_dynamic } diff --git a/test/gleam/erlang/charlist_test.gleam b/test/gleam/erlang/charlist_test.gleam index c68a225..00625ca 100644 --- a/test/gleam/erlang/charlist_test.gleam +++ b/test/gleam/erlang/charlist_test.gleam @@ -1,24 +1,23 @@ import gleam/dynamic import gleam/erlang/charlist -import gleam/should pub fn to_string_test() { - "Hello, from erlang!" - |> charlist.from_string() - |> charlist.to_string() - |> should.equal("Hello, from erlang!") + assert "Hello, from erlang!" = + "Hello, from erlang!" + |> charlist.from_string() + |> charlist.to_string() } pub fn empty_string_test() { - [] - |> dynamic.from - |> dynamic.unsafe_coerce - |> charlist.to_string - |> should.equal("") + assert "" = + [] + |> dynamic.from + |> dynamic.unsafe_coerce + |> charlist.to_string - "" - |> charlist.from_string() - |> dynamic.from - |> dynamic.unsafe_coerce - |> should.equal([]) + assert [] = + "" + |> charlist.from_string() + |> dynamic.from + |> dynamic.unsafe_coerce } diff --git a/test/gleam/erlang_test.gleam b/test/gleam/erlang_test.gleam index 27fd759..27a4f53 100644 --- a/test/gleam/erlang_test.gleam +++ b/test/gleam/erlang_test.gleam @@ -1,21 +1,22 @@ import gleam/dynamic import gleam/erlang -import gleam/should pub fn term_to_binary_test() { let term = dynamic.from(#(1, "2", <<"hello":utf8>>)) - term - |> erlang.term_to_binary() - |> erlang.binary_to_term() - |> should.equal(Ok(term)) + assert Ok(out) = + term + |> erlang.term_to_binary() + |> erlang.binary_to_term() + assert True = term == out - term - |> erlang.term_to_binary() - |> erlang.unsafe_binary_to_term() - |> should.equal(Ok(term)) + assert Ok(out) = + term + |> erlang.term_to_binary() + |> erlang.unsafe_binary_to_term() + assert True = term == out - <<>> - |> erlang.unsafe_binary_to_term() - |> should.equal(Error(Nil)) + assert Error(Nil) = + <<>> + |> erlang.unsafe_binary_to_term() }