aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-09-09 21:25:11 +0100
committerLouis Pilfold <louis@lpil.uk>2021-09-09 21:25:11 +0100
commitbb88a32d105da31e5cdd2ad713016c88b96ac0ce (patch)
tree5f233af161c86fcc3813bee4d006fe35125f5bdd
parent70136ec67863b0c97b9845edc13c6841eee033fc (diff)
downloadgleam_stdlib-bb88a32d105da31e5cdd2ad713016c88b96ac0ce.tar.gz
gleam_stdlib-bb88a32d105da31e5cdd2ad713016c88b96ac0ce.zip
JS dynamic element
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/gleam/dynamic.gleam49
-rw-r--r--src/gleam_stdlib.erl3
-rw-r--r--src/gleam_stdlib.js26
-rw-r--r--test/gleam/dynamic_test.gleam82
5 files changed, 109 insertions, 53 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 14654fc..5d1d19c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,8 @@
permissive to other null values.
- The `dynamic.result` function has been made more permissive to other result values.
- The `dynamic.thunk` function has been removed.
+- The `dynamic.element` label `postion` was renamed to `get`.
+- The `dynamic.element` now accepts negative indexes.
- The `io.get_line` function has been moved to the `gleam_erlang` library.
- The `atom` module has been moved to the `gleam_erlang` library.
- Prelude types like `Result`, `List` etc. are no longer redefined in their
diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam
index 903341a..d494550 100644
--- a/src/gleam/dynamic.gleam
+++ b/src/gleam/dynamic.gleam
@@ -341,27 +341,40 @@ if erlang {
///
pub external fn field(from: Dynamic, named: a) -> Result(Dynamic, DecodeError) =
"gleam_stdlib" "decode_field"
+}
- /// Checks to see if the Dynamic value is a tuple large enough to have a certain
- /// index, and return the value of that index if it is.
- ///
- /// ## Examples
- ///
- /// > element(from(#(1, 2)), 0)
- /// Ok(from(1))
- ///
- /// > element(from(#(1, 2)), 2)
- /// Error(DecodeError(expected: "3 element tuple", found: "2 element tuple"))
- ///
- /// > element(from(""), 2)
- /// Error(DecodeError(expected: "Tuple", found: "String"))
- ///
- pub external fn element(
- from: Dynamic,
- position: Int,
- ) -> Result(Dynamic, DecodeError) =
+/// Checks to see if the Dynamic value is a tuple large enough to have a certain
+/// index, and return the value of that index if it is.
+///
+/// ## Examples
+///
+/// > element(from(#(1, 2)), 0)
+/// Ok(from(1))
+///
+/// > element(from(#(1, 2)), 2)
+/// Error(DecodeError(expected: "3 element tuple", found: "2 element tuple"))
+///
+/// > element(from(""), 2)
+/// Error(DecodeError(expected: "Tuple", found: "String"))
+///
+pub fn element(
+ from data: Dynamic,
+ get index: Int,
+) -> Result(Dynamic, DecodeError) {
+ decode_element(data, index)
+}
+
+if erlang {
+ external fn decode_element(Dynamic, Int) -> Result(a, DecodeError) =
"gleam_stdlib" "decode_element"
+}
+if javascript {
+ external fn decode_element(Dynamic, Int) -> Result(a, DecodeError) =
+ "../gleam_stdlib.js" "decode_element"
+}
+
+if erlang {
/// Checks to see if the Dynamic value is a 2 element tuple.
///
/// If you do not wish to decode all the elements in the tuple use the
diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl
index 9429f71..92cae8e 100644
--- a/src/gleam_stdlib.erl
+++ b/src/gleam_stdlib.erl
@@ -111,7 +111,8 @@ decode_field(Data, Key) ->
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);
+ Msg = ["Tuple of at least ", integer_to_list(Position + 1), " elements"],
+ decode_error_msg(list_to_binary(Msg), Data);
Value ->
{ok, Value}
diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js
index 0982f62..9a80a6d 100644
--- a/src/gleam_stdlib.js
+++ b/src/gleam_stdlib.js
@@ -476,8 +476,12 @@ function classify_dynamic(data) {
return "List";
} else if (Number.isInteger(data)) {
return "Int";
+ } else if (Array.isArray(data)) {
+ return `Tuple of ${data.length} elements`;
} else if (BitString.isBitString(data)) {
return "BitString";
+ } else if (data instanceof Map) {
+ return "Map";
} else if (typeof data === "number") {
return "Float";
} else {
@@ -513,3 +517,25 @@ export function decode_bit_string(data) {
? new Ok(data)
: decoder_error("BitString", data);
}
+
+export function decode_element(data, index) {
+ let error = (size) =>
+ decoder_error(
+ `Tuple of at least ${size} element${size > 1 ? "s" : ""}`,
+ data
+ );
+ if (!Array.isArray(data)) return error(index < 0 ? 1 : index + 1);
+ if (index >= 0) {
+ if (index < data.length) {
+ return new Ok(data[index]);
+ } else {
+ return error(index + 1);
+ }
+ } else {
+ if (Math.abs(index) <= data.length) {
+ return new Ok(data[data.length + index]);
+ } else {
+ return error(Math.abs(index));
+ }
+ }
+}
diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam
index c65f98c..4ab3d65 100644
--- a/test/gleam/dynamic_test.gleam
+++ b/test/gleam/dynamic_test.gleam
@@ -1,11 +1,11 @@
import gleam/should
import gleam/dynamic.{DecodeError}
import gleam/bit_string
+import gleam/map
if erlang {
import gleam/list
import gleam/result
- import gleam/map
import gleam/option.{None, Some}
}
@@ -86,11 +86,6 @@ pub fn int_test() {
|> dynamic.from
|> dynamic.int
|> should.equal(Error(DecodeError(expected: "Int", found: "List")))
-
- <<1>>
- |> dynamic.from
- |> dynamic.int
- |> should.equal(Error(DecodeError(expected: "Int", found: "BitString")))
}
pub fn float_test() {
@@ -259,42 +254,61 @@ if erlang {
|> dynamic.field([])
|> should.be_error
}
+}
- pub fn element_test() {
- let ok_one_tuple = #("ok", 1)
+pub fn element_test() {
+ let ok_one_tuple = #("ok", 1)
- ok_one_tuple
- |> dynamic.from
- |> dynamic.element(0)
- |> should.equal(Ok(dynamic.from("ok")))
+ ok_one_tuple
+ |> dynamic.from
+ |> dynamic.element(0)
+ |> should.equal(Ok(dynamic.from("ok")))
- ok_one_tuple
- |> dynamic.from
- |> dynamic.element(1)
- |> should.equal(Ok(dynamic.from(1)))
+ ok_one_tuple
+ |> dynamic.from
+ |> dynamic.element(1)
+ |> should.equal(Ok(dynamic.from(1)))
- ok_one_tuple
- |> dynamic.from
- |> dynamic.element(2)
- |> should.be_error
+ ok_one_tuple
+ |> dynamic.from
+ |> dynamic.element(2)
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of at least 3 elements",
+ found: "Tuple of 2 elements",
+ )))
- ok_one_tuple
- |> dynamic.from
- |> dynamic.element(-1)
- |> should.be_error
+ ok_one_tuple
+ |> dynamic.from
+ |> dynamic.element(-1)
+ |> should.equal(Ok(dynamic.from(1)))
- 1
- |> dynamic.from
- |> dynamic.element(0)
- |> should.be_error
+ ok_one_tuple
+ |> dynamic.from
+ |> dynamic.element(-3)
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of at least 3 elements",
+ found: "Tuple of 2 elements",
+ )))
- map.new()
- |> map.insert(1, "ok")
- |> dynamic.from
- |> dynamic.element(0)
- |> should.be_error
- }
+ 1
+ |> dynamic.from
+ |> dynamic.element(0)
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of at least 1 element",
+ found: "Int",
+ )))
+
+ map.new()
+ |> map.insert(1, "ok")
+ |> dynamic.from
+ |> dynamic.element(0)
+ |> should.equal(Error(DecodeError(
+ expected: "Tuple of at least 1 element",
+ found: "Map",
+ )))
+}
+if erlang {
pub fn tuple2_test() {
#(1, 2)
|> dynamic.from