diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | src/gleam/dynamic.gleam | 230 | ||||
-rw-r--r-- | src/gleam_stdlib.erl | 15 | ||||
-rw-r--r-- | test/gleam/dynamic_test.gleam | 302 |
4 files changed, 547 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 48f759f..c8a6117 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- The `dynamic` module gains the `tuple3`, `tuple4`, `tuple5`, `tuple6` functions and + their typed equivalents `typed_tuple3`, `typed_tuple4`, `typed_tuple5`, `typed_tuple6`. - The `list` modules gains the `drop_while` and `take_while` functions. ## v0.14.0 - 2021-02-18 diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam index 55462b2..aa1ff5d 100644 --- a/src/gleam/dynamic.gleam +++ b/src/gleam/dynamic.gleam @@ -328,7 +328,7 @@ pub external fn element(from: Dynamic, position: Int) -> Result(Dynamic, String) /// > tuple2(from(tuple(1, 2))) /// Ok(tuple(from(1), from(2))) /// -/// > tuple2(from(tuple(1, 2))) +/// > tuple2(from(tuple(1, 2, 3))) /// Error("Expected a 2 element tuple") /// /// > tuple2(from("")) @@ -368,6 +368,234 @@ pub fn typed_tuple2( Ok(tuple(a, b)) } +/// Checks to see if the Dynamic value is a 3 element tuple. +/// +/// If you do not wish to decode all the elements in the tuple use the +/// `typed_tuple3` function instead. +/// +/// ## Examples +/// +/// > tuple3(from(tuple(1, 2, 3))) +/// Ok(tuple(from(1), from(2), from(3))) +/// +/// > tuple3(from(tuple(1, 2))) +/// Error("Expected a 3 element tuple") +/// +/// > tuple3(from("")) +/// Error("Expected a tuple, got a binary") +/// +pub external fn tuple3( + from: Dynamic, +) -> Result(tuple(Dynamic, Dynamic, Dynamic), String) = + "gleam_stdlib" "decode_tuple3" + +/// Checks to see if the Dynamic value is a 3 element tuple containing two +/// specifically typed elements. +/// +/// If you wish to decode all the elements in the list use the `typed_tuple3` +/// instead. +/// +/// ## Examples +/// +/// > typed_tuple3(from(tuple(1, 2, 3)), int, int, int) +/// Ok(tuple(1, 2, 3)) +/// +/// > typed_tuple3(from(tuple(1, 2.0, "3")), int, float, string) +/// Ok(tuple(1, 2.0, "3")) +/// +/// > typed_tuple3(from(tuple(1, 2)), int, float, string) +/// Error("Expected a 3 element tuple, got a 2 element tuple") +/// +/// > typed_tuple3(from(""), int, float, string) +/// Error("Expected a tuple, got a binary") +/// +pub fn typed_tuple3( + from tup: Dynamic, + first decode_first: Decoder(a), + second decode_second: Decoder(b), + third decode_third: Decoder(c), +) -> Result(tuple(a, b, c), String) { + try tuple(first, second, third) = tuple3(tup) + try a = decode_first(first) + try b = decode_second(second) + try c = decode_third(third) + Ok(tuple(a, b, c)) +} + +/// Checks to see if the Dynamic value is a 4 element tuple. +/// +/// If you do not wish to decode all the elements in the tuple use the +/// `typed_tuple4` function instead. +/// +/// ## Examples +/// +/// > tuple4(from(tuple(1, 2, 3, 4))) +/// Ok(tuple(from(1), from(2), from(3), from(4))) +/// +/// > tuple4(from(tuple(1, 2))) +/// Error("Expected a 4 element tuple") +/// +/// > tuple4(from("")) +/// Error("Expected a tuple, got a binary") +/// +pub external fn tuple4( + from: Dynamic, +) -> Result(tuple(Dynamic, Dynamic, Dynamic, Dynamic), String) = + "gleam_stdlib" "decode_tuple4" + +/// Checks to see if the Dynamic value is a 4 element tuple containing two +/// specifically typed elements. +/// +/// If you wish to decode all the elements in the list use the `typed_tuple4` +/// instead. +/// +/// ## Examples +/// +/// > typed_tuple4(from(tuple(1, 2, 3, 4)), int, int, int, int) +/// Ok(tuple(1, 2, 3, 4)) +/// +/// > typed_tuple4(from(tuple(1, 2.0, "3", 4)), int, float, string, int) +/// Ok(tuple(1, 2.0, "3", 4)) +/// +/// > typed_tuple4(from(tuple(1, 2)), int, float, string, int) +/// Error("Expected a 4 element tuple, got a 2 element tuple") +/// +/// > typed_tuple4(from(""), int, float, string, int) +/// Error("Expected a tuple, got a binary") +/// +pub fn typed_tuple4( + from tup: Dynamic, + first decode_first: Decoder(a), + second decode_second: Decoder(b), + third decode_third: Decoder(c), + fourth decode_fourth: Decoder(d), +) -> Result(tuple(a, b, c, d), String) { + try tuple(first, second, third, fourth) = tuple4(tup) + try a = decode_first(first) + try b = decode_second(second) + try c = decode_third(third) + try d = decode_fourth(fourth) + Ok(tuple(a, b, c, d)) +} + +/// Checks to see if the Dynamic value is a 5 element tuple. +/// +/// If you do not wish to decode all the elements in the tuple use the +/// `typed_tuple5` function instead. +/// +/// ## Examples +/// +/// > tuple5(from(tuple(1, 2, 3, 4, 5))) +/// Ok(tuple(from(1), from(2), from(3), from(4), from(5))) +/// +/// > tuple5(from(tuple(1, 2))) +/// Error("Expected a 5 element tuple") +/// +/// > tuple5(from("")) +/// Error("Expected a tuple, got a binary") +/// +pub external fn tuple5( + from: Dynamic, +) -> Result(tuple(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), String) = + "gleam_stdlib" "decode_tuple5" + +/// Checks to see if the Dynamic value is a 5 element tuple containing two +/// specifically typed elements. +/// +/// If you wish to decode all the elements in the list use the `typed_tuple5` +/// instead. +/// +/// ## Examples +/// +/// > typed_tuple5(from(tuple(1, 2, 3, 4, 5)), int, int, int, int, int) +/// Ok(tuple(1, 2, 3, 4, 5)) +/// +/// > typed_tuple5(from(tuple(1, 2.0, "3", 4, 5)), int, float, string, int, int) +/// Ok(tuple(1, 2.0, "3", 4, 5)) +/// +/// > typed_tuple5(from(tuple(1, 2)), int, float, string, int, int) +/// Error("Expected a 5 element tuple, got a 2 element tuple") +/// +/// > typed_tuple5(from(""), int, float, string, int, int) +/// Error("Expected a tuple, got a binary") +/// +pub fn typed_tuple5( + from tup: Dynamic, + first decode_first: Decoder(a), + second decode_second: Decoder(b), + third decode_third: Decoder(c), + fourth decode_fourth: Decoder(d), + fifth decode_fifth: Decoder(e), +) -> Result(tuple(a, b, c, d, e), String) { + try tuple(first, second, third, fourth, fifth) = tuple5(tup) + try a = decode_first(first) + try b = decode_second(second) + try c = decode_third(third) + try d = decode_fourth(fourth) + try e = decode_fifth(fifth) + Ok(tuple(a, b, c, d, e)) +} + +/// Checks to see if the Dynamic value is a 6 element tuple. +/// +/// If you do not wish to decode all the elements in the tuple use the +/// `typed_tuple6` function instead. +/// +/// ## Examples +/// +/// > tuple6(from(tuple(1, 2, 3, 4, 5, 6))) +/// Ok(tuple(from(1), from(2), from(3), from(4), from(5), from(6))) +/// +/// > tuple6(from(tuple(1, 2))) +/// Error("Expected a 6 element tuple") +/// +/// > tuple6(from("")) +/// Error("Expected a tuple, got a binary") +/// +pub external fn tuple6( + from: Dynamic, +) -> Result(tuple(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), String) = + "gleam_stdlib" "decode_tuple6" + +/// Checks to see if the Dynamic value is a 6 element tuple containing two +/// specifically typed elements. +/// +/// If you wish to decode all the elements in the list use the `typed_tuple6` +/// instead. +/// +/// ## Examples +/// +/// > typed_tuple6(from(tuple(1, 2, 3, 4, 5, 6)), int, int, int, int, int, int) +/// Ok(tuple(1, 2, 3, 4, 5, 6)) +/// +/// > typed_tuple6(from(tuple(1, 2.0, "3", 4, 5, 6)), int, float, string, int, int) +/// Ok(tuple(1, 2.0, "3", 4, 5, 6)) +/// +/// > typed_tuple6(from(tuple(1, 2)), int, float, string, int, int, int) +/// Error("Expected a 6 element tuple, got a 2 element tuple") +/// +/// > typed_tuple6(from(""), int, float, string, int, int, int) +/// Error("Expected a tuple, got a binary") +/// +pub fn typed_tuple6( + from tup: Dynamic, + first decode_first: Decoder(a), + second decode_second: Decoder(b), + third decode_third: Decoder(c), + fourth decode_fourth: Decoder(d), + fifth decode_fifth: Decoder(e), + sixth decode_sixth: Decoder(f), +) -> Result(tuple(a, b, c, d, e, f), String) { + try tuple(first, second, third, fourth, fifth, sixth) = tuple6(tup) + try a = decode_first(first) + try b = decode_second(second) + try c = decode_third(third) + try d = decode_fourth(fourth) + try e = decode_fifth(fifth) + try f = decode_sixth(sixth) + Ok(tuple(a, b, c, d, e, f)) +} + /// Checks to see if the Dynamic value is map. /// /// ## Examples diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl index e8cc7e4..10d3752 100644 --- a/src/gleam_stdlib.erl +++ b/src/gleam_stdlib.erl @@ -8,7 +8,8 @@ decode_thunk/1, decode_atom/1, decode_list/1, decode_field/2, decode_element/2, parse_int/1, parse_float/1, compare_strings/2, string_pop_grapheme/1, string_starts_with/2, string_ends_with/2, - string_pad/4, decode_tuple2/1, decode_map/1, bit_string_int_to_u32/1, + string_pad/4, decode_tuple2/1, decode_tuple3/1, decode_tuple4/1, + decode_tuple5/1, decode_tuple6/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_decode64/1, wrap_list/1, rescue/1, get_line/1]). @@ -56,6 +57,18 @@ classify(_) -> "some other type". decode_tuple2({_, _} = T) -> {ok, T}; decode_tuple2(Data) -> decode_error_msg("a 2 element tuple", Data). +decode_tuple3({_, _, _} = T) -> {ok, T}; +decode_tuple3(Data) -> decode_error_msg("a 3 element tuple", Data). + +decode_tuple4({_, _, _, _} = T) -> {ok, T}; +decode_tuple4(Data) -> decode_error_msg("a 4 element tuple", Data). + +decode_tuple5({_, _, _, _, _} = T) -> {ok, T}; +decode_tuple5(Data) -> decode_error_msg("a 5 element tuple", Data). + +decode_tuple6({_, _, _, _, _, _} = T) -> {ok, T}; +decode_tuple6(Data) -> decode_error_msg("a 6 element tuple", Data). + decode_map(Data) when is_map(Data) -> {ok, Data}; decode_map(Data) -> decode_error_msg("a map", Data). diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam index d8b3be4..50c5ac4 100644 --- a/test/gleam/dynamic_test.gleam +++ b/test/gleam/dynamic_test.gleam @@ -356,6 +356,308 @@ pub fn typed_tuple2_test() { |> should.equal(Error("Expected a 2 element tuple, got an int")) } +pub fn tuple3_test() { + tuple(1, 2, 3) + |> dynamic.from + |> dynamic.tuple3 + |> should.equal(Ok(tuple(dynamic.from(1), dynamic.from(2), dynamic.from(3)))) + + tuple(1, "", 3.0) + |> dynamic.from + |> dynamic.tuple3 + |> should.equal(Ok(tuple(dynamic.from(1), dynamic.from(""), dynamic.from(3.0)))) + + tuple(1, 2) + |> dynamic.from + |> dynamic.tuple3 + |> should.equal(Error("Expected a 3 element tuple, got a 2 element tuple")) + + 1 + |> dynamic.from + |> dynamic.tuple3 + |> should.equal(Error("Expected a 3 element tuple, got an int")) +} + +pub fn typed_tuple3_test() { + tuple(1, 2, 3) + |> dynamic.from + |> dynamic.typed_tuple3(dynamic.int, dynamic.int, dynamic.int) + |> should.equal(Ok(tuple(1, 2, 3))) + + tuple(1, "", 3.0) + |> dynamic.from + |> dynamic.typed_tuple3(dynamic.int, dynamic.string, dynamic.float) + |> should.equal(Ok(tuple(1, "", 3.0))) + + tuple(1, 2, "") + |> dynamic.from + |> dynamic.typed_tuple3(dynamic.int, dynamic.int, dynamic.int) + |> should.equal(Error("Expected an int, got a binary")) + + tuple(1, 2) + |> dynamic.from + |> dynamic.typed_tuple3(dynamic.int, dynamic.int, dynamic.int) + |> should.equal(Error("Expected a 3 element tuple, got a 2 element tuple")) + + 1 + |> dynamic.from + |> dynamic.typed_tuple3(dynamic.int, dynamic.int, dynamic.int) + |> should.equal(Error("Expected a 3 element tuple, got an int")) +} + +pub fn tuple4_test() { + tuple(1, 2, 3, 4) + |> dynamic.from + |> dynamic.tuple4 + |> should.equal(Ok(tuple( + dynamic.from(1), + dynamic.from(2), + dynamic.from(3), + dynamic.from(4), + ))) + + tuple(1, "", 3.0, 4) + |> dynamic.from + |> dynamic.tuple4 + |> should.equal(Ok(tuple( + dynamic.from(1), + dynamic.from(""), + dynamic.from(3.0), + dynamic.from(4), + ))) + + tuple(1, 2) + |> dynamic.from + |> dynamic.tuple4 + |> should.equal(Error("Expected a 4 element tuple, got a 2 element tuple")) + + 1 + |> dynamic.from + |> dynamic.tuple4 + |> should.equal(Error("Expected a 4 element tuple, got an int")) +} + +pub fn typed_tuple4_test() { + tuple(1, 2, 3, 4) + |> dynamic.from + |> dynamic.typed_tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) + |> should.equal(Ok(tuple(1, 2, 3, 4))) + + tuple(1, "", 3.0, 4) + |> dynamic.from + |> dynamic.typed_tuple4( + dynamic.int, + dynamic.string, + dynamic.float, + dynamic.int, + ) + |> should.equal(Ok(tuple(1, "", 3.0, 4))) + + tuple(1, 2, 3, "") + |> dynamic.from + |> dynamic.typed_tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) + |> should.equal(Error("Expected an int, got a binary")) + + tuple(1, 2) + |> dynamic.from + |> dynamic.typed_tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) + |> should.equal(Error("Expected a 4 element tuple, got a 2 element tuple")) + + 1 + |> dynamic.from + |> dynamic.typed_tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) + |> should.equal(Error("Expected a 4 element tuple, got an int")) +} + +pub fn tuple5_test() { + tuple(1, 2, 3, 4, 5) + |> dynamic.from + |> dynamic.tuple5 + |> should.equal(Ok(tuple( + dynamic.from(1), + dynamic.from(2), + dynamic.from(3), + dynamic.from(4), + dynamic.from(5), + ))) + + tuple(1, "", 3.0, 4, 5) + |> dynamic.from + |> dynamic.tuple5 + |> should.equal(Ok(tuple( + dynamic.from(1), + dynamic.from(""), + dynamic.from(3.0), + dynamic.from(4), + dynamic.from(5), + ))) + + tuple(1, 2) + |> dynamic.from + |> dynamic.tuple5 + |> should.equal(Error("Expected a 5 element tuple, got a 2 element tuple")) + + 1 + |> dynamic.from + |> dynamic.tuple5 + |> should.equal(Error("Expected a 5 element tuple, got an int")) +} + +pub fn typed_tuple5_test() { + tuple(1, 2, 3, 4, 5) + |> dynamic.from + |> dynamic.typed_tuple5( + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Ok(tuple(1, 2, 3, 4, 5))) + + tuple(1, "", 3.0, 4, 5) + |> dynamic.from + |> dynamic.typed_tuple5( + dynamic.int, + dynamic.string, + dynamic.float, + dynamic.int, + dynamic.int, + ) + |> should.equal(Ok(tuple(1, "", 3.0, 4, 5))) + + tuple(1, 2, 3, 4, "") + |> dynamic.from + |> dynamic.typed_tuple5( + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Error("Expected an int, got a binary")) + + tuple(1, 2) + |> dynamic.from + |> dynamic.typed_tuple5( + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Error("Expected a 5 element tuple, got a 2 element tuple")) + + 1 + |> dynamic.from + |> dynamic.typed_tuple5( + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Error("Expected a 5 element tuple, got an int")) +} + +pub fn tuple6_test() { + tuple(1, 2, 3, 4, 5, 6) + |> dynamic.from + |> dynamic.tuple6 + |> should.equal(Ok(tuple( + dynamic.from(1), + dynamic.from(2), + dynamic.from(3), + dynamic.from(4), + dynamic.from(5), + dynamic.from(6), + ))) + + tuple(1, "", 3.0, 4, 5, 6) + |> dynamic.from + |> dynamic.tuple6 + |> should.equal(Ok(tuple( + dynamic.from(1), + dynamic.from(""), + dynamic.from(3.0), + dynamic.from(4), + dynamic.from(5), + dynamic.from(6), + ))) + + tuple(1, 2) + |> dynamic.from + |> dynamic.tuple6 + |> should.equal(Error("Expected a 6 element tuple, got a 2 element tuple")) + + 1 + |> dynamic.from + |> dynamic.tuple6 + |> should.equal(Error("Expected a 6 element tuple, got an int")) +} + +pub fn typed_tuple6_test() { + tuple(1, 2, 3, 4, 5, 6) + |> dynamic.from + |> dynamic.typed_tuple6( + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Ok(tuple(1, 2, 3, 4, 5, 6))) + + tuple(1, "", 3.0, 4, 5, 6) + |> dynamic.from + |> dynamic.typed_tuple6( + dynamic.int, + dynamic.string, + dynamic.float, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Ok(tuple(1, "", 3.0, 4, 5, 6))) + + tuple(1, 2, 3, 4, 5, "") + |> dynamic.from + |> dynamic.typed_tuple6( + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Error("Expected an int, got a binary")) + + tuple(1, 2) + |> dynamic.from + |> dynamic.typed_tuple6( + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Error("Expected a 6 element tuple, got a 2 element tuple")) + + 1 + |> dynamic.from + |> dynamic.typed_tuple6( + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + dynamic.int, + ) + |> should.equal(Error("Expected a 6 element tuple, got an int")) +} + pub fn map_test() { map.new() |> dynamic.from |