aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2020-07-15 11:38:16 +0100
committerLouis Pilfold <louis@lpil.uk>2020-07-15 11:38:16 +0100
commit36232a3920aa4aaca667d66dfbedfb2d4227a114 (patch)
treeaddf9ca942c117ab8ca9d8b1fe1532563da5dc01
parent48d458d4f5785607fa236fd70e8dd773ab014699 (diff)
downloadgleam_stdlib-36232a3920aa4aaca667d66dfbedfb2d4227a114.tar.gz
gleam_stdlib-36232a3920aa4aaca667d66dfbedfb2d4227a114.zip
function.rescue
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/gleam/function.gleam18
-rw-r--r--src/gleam_stdlib.erl97
-rw-r--r--test/gleam/function_test.gleam18
4 files changed, 88 insertions, 46 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 508db58..36e0a52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
- The `dynamic` module gains the `any` function.
- The `bit_builder` module gains the `from_string` function.
- The `list` module gains the `key_set` function.
+- The `function` module gains the `rescue` function.
## v0.10.1 - 2020-07-01
diff --git a/src/gleam/function.gleam b/src/gleam/function.gleam
index b92c0ac..70da466 100644
--- a/src/gleam/function.gleam
+++ b/src/gleam/function.gleam
@@ -1,3 +1,5 @@
+import gleam/dynamic.{Dynamic}
+
/// Takes two functions and chains them together to form one function that takes
/// the input from the first and returns the output of the second.
///
@@ -17,3 +19,19 @@ pub fn flip(fun: fn(a, b) -> c) -> fn(b, a) -> c {
pub fn identity(x: a) -> a {
x
}
+
+pub type Exception {
+ Exited(Dynamic)
+ Thrown(Dynamic)
+ Errored(Dynamic)
+}
+
+/// 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, Exception) =
+ "gleam_stdlib" "rescue"
diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl
index fa7f683..7d5b54f 100644
--- a/src/gleam_stdlib.erl
+++ b/src/gleam_stdlib.erl
@@ -11,7 +11,7 @@
string_pad/4, decode_tuple2/1, decode_map/1, bit_string_int_to_u32/1,
bit_string_int_from_u32/1, bit_string_append/2, bit_string_part_/3,
decode_bit_string/1, compile_regex/2, regex_match/2, regex_split/2,
- regex_scan/2, base_decoded4/1, wrap_list/1]).
+ regex_scan/2, base_decoded4/1, wrap_list/1, rescue/1]).
should_equal(Actual, Expected) -> ?assertEqual(Expected, Actual).
should_not_equal(Actual, Expected) -> ?assertNotEqual(Expected, Actual).
@@ -19,21 +19,21 @@ should_be_ok(A) -> ?assertMatch({ok, _}, A).
should_be_error(A) -> ?assertMatch({error, _}, A).
map_get(Map, Key) ->
- case maps:find(Key, Map) of
- error -> {error, nil};
- OkFound -> OkFound
- end.
+ case maps:find(Key, Map) of
+ error -> {error, nil};
+ OkFound -> OkFound
+ end.
atom_create_from_string(S) ->
- binary_to_atom(S, utf8).
+ binary_to_atom(S, utf8).
atom_to_string(S) ->
- atom_to_binary(S, utf8).
+ atom_to_binary(S, utf8).
atom_from_string(S) ->
- try {ok, binary_to_existing_atom(S, utf8)} catch
- error:badarg -> {error, atom_not_loaded}
- end.
+ try {ok, binary_to_existing_atom(S, utf8)}
+ catch error:badarg -> {error, atom_not_loaded}
+ end.
iodata_append(Iodata, String) -> [Iodata, String].
iodata_prepend(Iodata, String) -> [String, Iodata].
@@ -41,7 +41,7 @@ iodata_prepend(Iodata, String) -> [String, Iodata].
identity(X) -> X.
decode_error_msg(Type, Data) ->
- {error, iolist_to_binary(io_lib:format("Expected ~s, got ~s", [Type, classify(Data)]))}.
+ {error, iolist_to_binary(io_lib:format("Expected ~s, got ~s", [Type, classify(Data)]))}.
classify(X) when is_atom(X) -> "an atom";
classify(X) when is_binary(X) -> "a binary";
@@ -81,45 +81,42 @@ decode_list(Data) when is_list(Data) -> {ok, Data};
decode_list(Data) -> decode_error_msg("a list", Data).
decode_field(Data, Key) ->
- case Data of
- #{Key := Value} ->
- {ok, Value};
+ case Data of
+ #{Key := Value} ->
+ {ok, Value};
- _ ->
- decode_error_msg(io_lib:format("a map with key `~p`", [Key]), Data)
- end.
+ _ ->
+ decode_error_msg(io_lib:format("a map with key `~p`", [Key]), Data)
+ end.
decode_element(Data, Position) when is_tuple(Data) ->
- case catch element(Position + 1, Data) of
- {'EXIT', _Reason} ->
- decode_error_msg(["a tuple of at least ", integer_to_list(Position + 1), " size"], Data);
+ case catch element(Position + 1, Data) of
+ {'EXIT', _Reason} ->
+ decode_error_msg(["a tuple of at least ", integer_to_list(Position + 1), " size"], Data);
- Value ->
- {ok, Value}
- end;
+ Value ->
+ {ok, Value}
+ end;
decode_element(Data, _Position) -> decode_error_msg("a tuple", Data).
parse_int(String) ->
- case catch binary_to_integer(String) of
- Int when is_integer(Int) -> {ok, Int};
- _ -> {error, nil}
- end.
+ case catch binary_to_integer(String) of
+ Int when is_integer(Int) -> {ok, Int};
+ _ -> {error, nil}
+ end.
parse_float(String) ->
- case catch binary_to_float(String) of
- Float when is_float(Float) -> {ok, Float};
- _ -> {error, nil}
- end.
+ case catch binary_to_float(String) of
+ Float when is_float(Float) -> {ok, Float};
+ _ -> {error, nil}
+ end.
compare_strings(Lhs, Rhs) ->
- if
- Lhs == Rhs ->
- eq;
- Lhs < Rhs ->
- lt;
- true ->
- gt
- end.
+ if
+ Lhs == Rhs -> eq;
+ Lhs < Rhs -> lt;
+ true -> gt
+ end.
string_starts_with(_, <<>>) -> true;
string_starts_with(String, Prefix) when byte_size(Prefix) > byte_size(String) -> false;
@@ -144,12 +141,12 @@ string_pop_grapheme(String) ->
end.
bit_string_append(First, Second) ->
- <<First/bitstring, Second/bitstring>>.
+ <<First/bitstring, Second/bitstring>>.
bit_string_part_(Bin, Pos, Len) ->
- try {ok, binary:part(Bin, Pos, Len)} catch
- error:badarg -> {error, nil}
- end.
+ try {ok, binary:part(Bin, Pos, Len)}
+ catch error:badarg -> {error, nil}
+ end.
bit_string_int_to_u32(I) when 0 =< I, I < 4294967296 ->
{ok, <<I:32>>};
@@ -199,9 +196,17 @@ regex_scan(Regex, String) ->
end.
base_decoded4(S) ->
- try {ok, base64:decode(S)}
- catch error:badarith -> {error, nil}
- end.
+ try {ok, base64:decode(S)}
+ catch error:badarith -> {error, nil}
+ end.
wrap_list(X) when is_list(X) -> X;
wrap_list(X) -> [X].
+
+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/function_test.gleam b/test/gleam/function_test.gleam
index eb94581..f5d34a9 100644
--- a/test/gleam/function_test.gleam
+++ b/test/gleam/function_test.gleam
@@ -1,4 +1,5 @@
import gleam/should
+import gleam/dynamic
import gleam/function
import gleam/int
import gleam/list
@@ -65,3 +66,20 @@ pub fn identity_test() {
|> function.identity
|> should.equal(tuple(1, 2.0))
}
+
+external fn throw(a) -> Nil =
+ "erlang" "throw"
+
+external fn raise_error(a) -> Nil =
+ "erlang" "error"
+
+pub fn rescue_test() {
+ function.rescue(fn() { 1 })
+ |> should.equal(Ok(1))
+
+ function.rescue(fn() { throw(1) })
+ |> should.equal(Error(function.Thrown(dynamic.from(1))))
+
+ function.rescue(fn() { raise_error("") })
+ |> should.equal(Error(function.Errored(dynamic.from(""))))
+}