aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2022-01-01 23:17:49 +0000
committerLouis Pilfold <louis@lpil.uk>2022-01-01 23:17:49 +0000
commit54d0699156d3d2ab7ac70c4099c7665381ba01b0 (patch)
treecf3602b502d90e78b47c564bb1fc283146e4fbd0
parent1956e6acce411d9986f3dd43ff306de4735603ee (diff)
downloadgleam_stdlib-54d0699156d3d2ab7ac70c4099c7665381ba01b0.tar.gz
gleam_stdlib-54d0699156d3d2ab7ac70c4099c7665381ba01b0.zip
Add path to DecodeError, return multiple decode errors
-rw-r--r--CHANGELOG.md3
-rw-r--r--src/gleam/dynamic.gleam220
-rw-r--r--src/gleam/list.gleam4
-rw-r--r--src/gleam_stdlib.erl2
-rw-r--r--src/gleam_stdlib.mjs4
-rw-r--r--test/gleam/dynamic_test.gleam211
6 files changed, 245 insertions, 199 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 186c64a..a74ee6a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,8 +8,7 @@
- The `dynamic.list` function has been renamed to `dynamic.shallow_list`.
- The `dynamic.typed_list` function has been renamed to `dynamic.list`.
- The `dynamic.typed_result` function has been renamed to `dynamic.result`.
-- The `dynamic.any` is now available on JavaScript and has correct information
- in any errors returned.
+- The `dynamic.any` is now available on JavaScript.
- The `dynamic.typed_tuple*` functions have been renamed to `dynamic.tuple*`.
- The `dynamic.field` and `dynamic.element` functions now requires the type of
the field to be specified.
diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam
index 82cfa2f..81a7a92 100644
--- a/src/gleam/dynamic.gleam
+++ b/src/gleam/dynamic.gleam
@@ -15,11 +15,14 @@ pub external type Dynamic
/// Error returned when unexpected data is encountered
pub type DecodeError {
- DecodeError(expected: String, found: String)
+ DecodeError(expected: String, found: String, path: List(String))
}
+pub type DecodeErrors =
+ List(DecodeError)
+
pub type Decoder(t) =
- fn(Dynamic) -> Result(t, DecodeError)
+ fn(Dynamic) -> Result(t, DecodeErrors)
/// Converts any Gleam data into `Dynamic` data.
///
@@ -77,19 +80,19 @@ pub fn dynamic(term: Dynamic) -> Dynamic {
/// True
///
/// > bit_string(from(123))
-/// Error(DecodeError(expected: "BitString", found: "Int"))
+/// Error(DecodeError(expected: "BitString", found: "Int", path: []))
///
-pub fn bit_string(from data: Dynamic) -> Result(BitString, DecodeError) {
+pub fn bit_string(from data: Dynamic) -> Result(BitString, DecodeErrors) {
decode_bit_string(data)
}
if erlang {
- external fn decode_bit_string(Dynamic) -> Result(BitString, DecodeError) =
+ external fn decode_bit_string(Dynamic) -> Result(BitString, DecodeErrors) =
"gleam_stdlib" "decode_bit_string"
}
if javascript {
- external fn decode_bit_string(Dynamic) -> Result(BitString, DecodeError) =
+ external fn decode_bit_string(Dynamic) -> Result(BitString, DecodeErrors) =
"../gleam_stdlib.mjs" "decode_bit_string"
}
@@ -102,27 +105,35 @@ if javascript {
/// Ok("Hello")
///
/// > string(from(123))
-/// Error(DecodeError(expected: "String", found: "Int"))
+/// Error(DecodeError(expected: "String", found: "Int", path: []))
///
-pub fn string(from data: Dynamic) -> Result(String, DecodeError) {
+pub fn string(from data: Dynamic) -> Result(String, DecodeErrors) {
decode_string(data)
}
+fn map_errors(
+ result: Result(t, DecodeErrors),
+ f: fn(DecodeError) -> DecodeError,
+) -> Result(t, DecodeErrors) {
+ result.map_error(result, list.map(_, f))
+}
+
if erlang {
- fn decode_string(data: Dynamic) -> Result(String, DecodeError) {
+ fn decode_string(data: Dynamic) -> Result(String, DecodeErrors) {
bit_string(data)
- |> result.map_error(fn(error) { DecodeError(..error, expected: "String") })
+ |> map_errors(put_expected(_, "String"))
|> result.then(fn(raw) {
case bit_string.to_string(raw) {
Ok(string) -> Ok(string)
- Error(Nil) -> Error(DecodeError(expected: "String", found: "BitString"))
+ Error(Nil) ->
+ Error([DecodeError(expected: "String", found: "BitString", path: [])])
}
})
}
}
if javascript {
- external fn decode_string(Dynamic) -> Result(String, DecodeError) =
+ external fn decode_string(Dynamic) -> Result(String, DecodeErrors) =
"../gleam_stdlib.mjs" "decode_string"
}
@@ -156,19 +167,19 @@ if javascript {
/// Ok(123)
///
/// > int(from("Hello"))
-/// Error(DecodeError(expected: "Int", found: "String"))
+/// Error(DecodeError(expected: "Int", found: "String", path: []))
///
-pub fn int(from data: Dynamic) -> Result(Int, DecodeError) {
+pub fn int(from data: Dynamic) -> Result(Int, DecodeErrors) {
decode_int(data)
}
if erlang {
- external fn decode_int(Dynamic) -> Result(Int, DecodeError) =
+ external fn decode_int(Dynamic) -> Result(Int, DecodeErrors) =
"gleam_stdlib" "decode_int"
}
if javascript {
- external fn decode_int(Dynamic) -> Result(Int, DecodeError) =
+ external fn decode_int(Dynamic) -> Result(Int, DecodeErrors) =
"../gleam_stdlib.mjs" "decode_int"
}
@@ -181,19 +192,19 @@ if javascript {
/// Ok(2.0)
///
/// > float(from(123))
-/// Error(DecodeError(expected: "Float", found: "Int"))
+/// Error(DecodeError(expected: "Float", found: "Int", path: []))
///
-pub fn float(from data: Dynamic) -> Result(Float, DecodeError) {
+pub fn float(from data: Dynamic) -> Result(Float, DecodeErrors) {
decode_float(data)
}
if erlang {
- external fn decode_float(Dynamic) -> Result(Float, DecodeError) =
+ external fn decode_float(Dynamic) -> Result(Float, DecodeErrors) =
"gleam_stdlib" "decode_float"
}
if javascript {
- external fn decode_float(Dynamic) -> Result(Float, DecodeError) =
+ external fn decode_float(Dynamic) -> Result(Float, DecodeErrors) =
"../gleam_stdlib.mjs" "decode_float"
}
@@ -206,19 +217,19 @@ if javascript {
/// Ok(True)
///
/// > bool(from(123))
-/// Error(DecodeError(expected: "bool", found: "Int"))
+/// Error(DecodeError(expected: "bool", found: "Int", path: []))
///
-pub fn bool(from data: Dynamic) -> Result(Bool, DecodeError) {
+pub fn bool(from data: Dynamic) -> Result(Bool, DecodeErrors) {
decode_bool(data)
}
if erlang {
- external fn decode_bool(Dynamic) -> Result(Bool, DecodeError) =
+ external fn decode_bool(Dynamic) -> Result(Bool, DecodeErrors) =
"gleam_stdlib" "decode_bool"
}
if javascript {
- external fn decode_bool(Dynamic) -> Result(Bool, DecodeError) =
+ external fn decode_bool(Dynamic) -> Result(Bool, DecodeErrors) =
"../gleam_stdlib.mjs" "decode_bool"
}
@@ -234,29 +245,29 @@ if javascript {
/// Ok([from("a"), from("b"), from("c")])
///
/// > shallow_list(1)
-/// Error(DecodeError(expected: "Int", found: "Int"))
+/// Error(DecodeError(expected: "Int", found: "Int", path: []))
///
-pub fn shallow_list(from value: Dynamic) -> Result(List(Dynamic), DecodeError) {
+pub fn shallow_list(from value: Dynamic) -> Result(List(Dynamic), DecodeErrors) {
decode_list(value)
}
if erlang {
- external fn decode_list(Dynamic) -> Result(List(Dynamic), DecodeError) =
+ external fn decode_list(Dynamic) -> Result(List(Dynamic), DecodeErrors) =
"gleam_stdlib" "decode_list"
}
if javascript {
- external fn decode_list(Dynamic) -> Result(List(Dynamic), DecodeError) =
+ external fn decode_list(Dynamic) -> Result(List(Dynamic), DecodeErrors) =
"../gleam_stdlib.mjs" "decode_list"
}
if erlang {
- external fn decode_result(Dynamic) -> Result(Result(a, e), DecodeError) =
+ external fn decode_result(Dynamic) -> Result(Result(a, e), DecodeErrors) =
"gleam_stdlib" "decode_result"
}
if javascript {
- external fn decode_result(Dynamic) -> Result(Result(a, e), DecodeError) =
+ external fn decode_result(Dynamic) -> Result(Result(a, e), DecodeErrors) =
"../gleam_stdlib.mjs" "decode_result"
}
@@ -275,13 +286,13 @@ if javascript {
/// Ok(Error("boom"))
///
/// > result(of: from(123), ok: int, error: string)
-/// Error(DecodeError(expected: "2 element tuple", found: "Int"))
+/// Error(DecodeError(expected: "2 element tuple", found: "Int", path: []))
///
pub fn result(
of dynamic: Dynamic,
ok decode_ok: Decoder(a),
error decode_error: Decoder(e),
-) -> Result(Result(a, e), DecodeError) {
+) -> Result(Result(a, e), DecodeErrors) {
try inner_result = decode_result(dynamic)
case inner_result {
@@ -312,15 +323,15 @@ pub fn result(
/// Ok(["a", "b", "c"])
///
/// > list(from([1, 2, 3]), of: string)
-/// Error(DecodeError(expected: "String", found: "Int"))
+/// Error(DecodeError(expected: "String", found: "Int", path: []))
///
/// > list(from("ok"), of: string)
-/// Error(DecodeError(expected: "List", found: "String"))
+/// Error(DecodeError(expected: "List", found: "String", path: []))
///
pub fn list(
from dynamic: Dynamic,
- of decoder_type: fn(Dynamic) -> Result(inner, DecodeError),
-) -> Result(List(inner), DecodeError) {
+ of decoder_type: fn(Dynamic) -> Result(inner, DecodeErrors),
+) -> Result(List(inner), DecodeErrors) {
dynamic
|> shallow_list
|> result.then(list.try_map(_, decoder_type))
@@ -347,12 +358,12 @@ pub fn list(
/// Ok(None)
///
/// > option(from(123), string)
-/// Error(DecodeError(expected: "BitString", found: "Int"))
+/// Error(DecodeError(expected: "BitString", found: "Int", path: []))
///
pub fn optional(
from value: Dynamic,
of decode: Decoder(inner),
-) -> Result(Option(inner), DecodeError) {
+) -> Result(Option(inner), DecodeErrors) {
decode_optional(value, decode)
}
@@ -360,7 +371,7 @@ if erlang {
external fn decode_optional(
Dynamic,
Decoder(a),
- ) -> Result(Option(a), DecodeError) =
+ ) -> Result(Option(a), DecodeErrors) =
"gleam_stdlib" "decode_option"
}
@@ -368,7 +379,7 @@ if javascript {
external fn decode_optional(
Dynamic,
Decoder(a),
- ) -> Result(Option(a), DecodeError) =
+ ) -> Result(Option(a), DecodeErrors) =
"../gleam_stdlib.mjs" "decode_option"
}
@@ -384,24 +395,24 @@ if javascript {
/// Ok(Dynamic)
///
/// > field(from(123), "Hello")
-/// Error(DecodeError(expected: "Map", found: "Int"))
+/// Error(DecodeError(expected: "Map", found: "Int", path: []))
///
pub fn field(
from value: Dynamic,
named name: a,
of inner_type: Decoder(t),
-) -> Result(t, DecodeError) {
+) -> Result(t, DecodeErrors) {
try value = decode_field(value, name)
inner_type(value)
}
if erlang {
- external fn decode_field(Dynamic, name) -> Result(Dynamic, DecodeError) =
+ external fn decode_field(Dynamic, name) -> Result(Dynamic, DecodeErrors) =
"gleam_stdlib" "decode_field"
}
if javascript {
- external fn decode_field(Dynamic, name) -> Result(Dynamic, DecodeError) =
+ external fn decode_field(Dynamic, name) -> Result(Dynamic, DecodeErrors) =
"../gleam_stdlib.mjs" "decode_field"
}
@@ -414,16 +425,16 @@ if javascript {
/// Ok(from(1))
///
/// > element(from(#(1, 2)), 2)
-/// Error(DecodeError(expected: "3 element tuple", found: "2 element tuple"))
+/// Error(DecodeError(expected: "3 element tuple", found: "2 element tuple", path: []))
///
/// > element(from(""), 2)
-/// Error(DecodeError(expected: "Tuple", found: "String"))
+/// Error(DecodeError(expected: "Tuple", found: "String", path: []))
///
pub fn element(
from data: Dynamic,
at index: Int,
of inner_type: Decoder(t),
-) -> Result(t, DecodeError) {
+) -> Result(t, DecodeErrors) {
try tuple = decode_tuple(data)
let size = tuple_size(tuple)
try data = case index >= 0 {
@@ -441,41 +452,43 @@ pub fn element(
inner_type(data)
}
-fn exact_decode_tuple_error(size: Int, data: Dynamic) -> Result(a, DecodeError) {
+fn exact_decode_tuple_error(size: Int, data: Dynamic) -> Result(a, DecodeErrors) {
let s = case size {
0 -> ""
_ -> "s"
}
- ["Tuple of ", int.to_string(size), " element", s]
- |> string_builder.from_strings
- |> string_builder.to_string
- |> DecodeError(found: classify(data))
- |> Error
+ let error =
+ ["Tuple of ", int.to_string(size), " element", s]
+ |> string_builder.from_strings
+ |> string_builder.to_string
+ |> DecodeError(found: classify(data), path: [])
+ Error([error])
}
fn at_least_decode_tuple_error(
size: Int,
data: Dynamic,
-) -> Result(a, DecodeError) {
+) -> Result(a, DecodeErrors) {
let s = case size {
0 -> ""
_ -> "s"
}
- ["Tuple of at least ", int.to_string(size), " element", s]
- |> string_builder.from_strings
- |> string_builder.to_string
- |> DecodeError(found: classify(data))
- |> Error
+ let error =
+ ["Tuple of at least ", int.to_string(size), " element", s]
+ |> string_builder.from_strings
+ |> string_builder.to_string
+ |> DecodeError(found: classify(data), path: [])
+ Error([error])
}
// A tuple of unknown size
external type UnknownTuple
if erlang {
- external fn decode_tuple(Dynamic) -> Result(UnknownTuple, DecodeError) =
+ external fn decode_tuple(Dynamic) -> Result(UnknownTuple, DecodeErrors) =
"gleam_stdlib" "decode_tuple"
- external fn tuple_get(UnknownTuple, Int) -> Result(Dynamic, DecodeError) =
+ external fn tuple_get(UnknownTuple, Int) -> Result(Dynamic, DecodeErrors) =
"gleam_stdlib" "tuple_get"
external fn tuple_size(UnknownTuple) -> Int =
@@ -483,10 +496,10 @@ if erlang {
}
if javascript {
- external fn decode_tuple(Dynamic) -> Result(UnknownTuple, DecodeError) =
+ external fn decode_tuple(Dynamic) -> Result(UnknownTuple, DecodeErrors) =
"../gleam_stdlib.mjs" "decode_tuple"
- external fn tuple_get(UnknownTuple, Int) -> Result(Dynamic, DecodeError) =
+ external fn tuple_get(UnknownTuple, Int) -> Result(Dynamic, DecodeErrors) =
"../gleam_stdlib.mjs" "tuple_get"
external fn tuple_size(UnknownTuple) -> Int =
@@ -505,16 +518,16 @@ if javascript {
/// Ok(#(1, 2.0))
///
/// > tuple2(from(#(1, 2, 3)), int, float)
-/// Error(DecodeError(expected: "2 element tuple", found: "3 element tuple"))
+/// Error(DecodeError(expected: "2 element tuple", found: "3 element tuple", path: []))
///
/// > tuple2(from(""), int, float)
-/// Error(DecodeError(expected: "2 element tuple", found: "String"))
+/// Error(DecodeError(expected: "2 element tuple", found: "String", path: []))
///
pub fn tuple2(
from value: Dynamic,
first decode_first: Decoder(a),
second decode_second: Decoder(b),
-) -> Result(#(a, b), DecodeError) {
+) -> Result(#(a, b), DecodeErrors) {
try _ = assert_is_tuple(value, 2)
let #(first, second) = unsafe_coerce(value)
try a = decode_first(first)
@@ -525,28 +538,22 @@ pub fn tuple2(
fn assert_is_tuple(
value: Dynamic,
desired_size: Int,
-) -> Result(Nil, DecodeError) {
+) -> Result(Nil, DecodeErrors) {
let expected =
string_builder.to_string(string_builder.from_strings([
"Tuple of ",
int.to_string(desired_size),
" elements",
]))
- try tuple = put_expected(decode_tuple(value), expected)
+ try tuple = map_errors(decode_tuple(value), put_expected(_, expected))
case tuple_size(tuple) {
size if size == desired_size -> Ok(Nil)
_ -> exact_decode_tuple_error(desired_size, value)
}
}
-fn put_expected(
- result: Result(a, DecodeError),
- expected: String,
-) -> Result(a, DecodeError) {
- case result {
- Ok(_) -> result
- Error(e) -> Error(DecodeError(..e, expected: expected))
- }
+fn put_expected(error: DecodeError, expected: String) -> DecodeError {
+ DecodeError(..error, expected: expected)
}
/// Checks to see if a `Dynamic` value is a 3-element tuple containing
@@ -561,17 +568,17 @@ fn put_expected(
/// Ok(#(1, 2.0, "3"))
///
/// > tuple3(from(#(1, 2)), int, float, string)
-/// Error(DecodeError(expected: "3 element tuple", found: "2 element tuple"))
+/// Error(DecodeError(expected: "3 element tuple", found: "2 element tuple", path: []))
///
/// > tuple3(from(""), int, float, string)
-/// Error(DecodeError(expected: "3 element tuple", found: "String"))
+/// Error(DecodeError(expected: "3 element tuple", found: "String", path: []))
///
pub fn tuple3(
from value: Dynamic,
first decode_first: Decoder(a),
second decode_second: Decoder(b),
third decode_third: Decoder(c),
-) -> Result(#(a, b, c), DecodeError) {
+) -> Result(#(a, b, c), DecodeErrors) {
try _ = assert_is_tuple(value, 3)
let #(first, second, third) = unsafe_coerce(value)
try a = decode_first(first)
@@ -593,10 +600,10 @@ pub fn tuple3(
///
/// > tuple4(from(#(1, 2)), int, float, string, int)
/// Error("Expected a 4 element tuple, found a 2 element tuple")
-/// Error(DecodeError(expected: "4 element tuple", found: "2 element tuple"))
+/// Error(DecodeError(expected: "4 element tuple", found: "2 element tuple", path: []))
///
/// > tuple4(from(""), int, float, string, int)
-/// Error(DecodeError(expected: "4 element tuple", found: "String"))
+/// Error(DecodeError(expected: "4 element tuple", found: "String", path: []))
///
pub fn tuple4(
from value: Dynamic,
@@ -604,7 +611,7 @@ pub fn tuple4(
second decode_second: Decoder(b),
third decode_third: Decoder(c),
fourth decode_fourth: Decoder(d),
-) -> Result(#(a, b, c, d), DecodeError) {
+) -> Result(#(a, b, c, d), DecodeErrors) {
try _ = assert_is_tuple(value, 4)
let #(first, second, third, fourth) = unsafe_coerce(value)
try a = decode_first(first)
@@ -626,10 +633,10 @@ pub fn tuple4(
/// Ok(#(1, 2.0, "3", 4, 5))
///
/// > tuple5(from(#(1, 2)), int, float, string, int, int)
-/// Error(DecodeError(expected: "5 element tuple", found: "2 element tuple"))
+/// Error(DecodeError(expected: "5 element tuple", found: "2 element tuple", path: []))
///
/// > tuple5(from(""), int, float, string, int, int)
-/// Error(DecodeError(expected: "5 element tuple", found: "String"))
+/// Error(DecodeError(expected: "5 element tuple", found: "String", path: []))
///
pub fn tuple5(
from value: Dynamic,
@@ -638,7 +645,7 @@ pub fn tuple5(
third decode_third: Decoder(c),
fourth decode_fourth: Decoder(d),
fifth decode_fifth: Decoder(e),
-) -> Result(#(a, b, c, d, e), DecodeError) {
+) -> Result(#(a, b, c, d, e), DecodeErrors) {
try _ = assert_is_tuple(value, 5)
let #(first, second, third, fourth, fifth) = unsafe_coerce(value)
try a = decode_first(first)
@@ -661,10 +668,10 @@ pub fn tuple5(
/// Ok(#(1, 2.0, "3", 4, 5, 6))
///
/// > tuple6(from(#(1, 2)), int, float, string, int, int, int)
-/// Error(DecodeError(expected: "6 element tuple", found: "2 element tuple"))
+/// Error(DecodeError(expected: "6 element tuple", found: "2 element tuple", path: []))
///
/// > tuple6(from(""), int, float, string, int, int, int)
-/// Error(DecodeError(expected: "6 element tuple", found: "String"))
+/// Error(DecodeError(expected: "6 element tuple", found: "String", path: []))
///
pub fn tuple6(
from value: Dynamic,
@@ -674,7 +681,7 @@ pub fn tuple6(
fourth decode_fourth: Decoder(d),
fifth decode_fifth: Decoder(e),
sixth decode_sixth: Decoder(f),
-) -> Result(#(a, b, c, d, e, f), DecodeError) {
+) -> Result(#(a, b, c, d, e, f), DecodeErrors) {
try _ = assert_is_tuple(value, 6)
let #(first, second, third, fourth, fifth, sixth) = unsafe_coerce(value)
try a = decode_first(first)
@@ -695,22 +702,22 @@ pub fn tuple6(
/// Ok(map.new())
///
/// > map(from(1))
-/// Error(DecodeError(expected: "Map", found: "Int"))
+/// Error(DecodeError(expected: "Map", found: "Int", path: []))
///
/// > map(from(""))
-/// Error(DecodeError(expected: "Map", found: "String"))
+/// Error(DecodeError(expected: "Map", found: "String", path: []))
///
-pub fn map(from value: Dynamic) -> Result(Map(Dynamic, Dynamic), DecodeError) {
+pub fn map(from value: Dynamic) -> Result(Map(Dynamic, Dynamic), DecodeErrors) {
decode_map(value)
}
if erlang {
- external fn decode_map(Dynamic) -> Result(Map(Dynamic, Dynamic), DecodeError) =
+ external fn decode_map(Dynamic) -> Result(Map(Dynamic, Dynamic), DecodeErrors) =
"gleam_stdlib" "decode_map"
}
if javascript {
- external fn decode_map(Dynamic) -> Result(Map(Dynamic, Dynamic), DecodeError) =
+ external fn decode_map(Dynamic) -> Result(Map(Dynamic, Dynamic), DecodeErrors) =
"../gleam_stdlib.mjs" "decode_map"
}
@@ -731,35 +738,22 @@ if javascript {
/// Ok("a bool")
///
/// > bool_or_string(from(1))
-/// Error(DecodeError(expected: "unknown", found: "unknown"))
+/// Error(DecodeError(expected: "unknown", found: "unknown", path: []))
///
pub fn any(
from data: Dynamic,
of decoders: List(Decoder(t)),
-) -> Result(t, DecodeError) {
- do_any(data, decoders, [])
-}
-
-fn do_any(
- data: Dynamic,
- decoders: List(Decoder(a)),
- failed: List(String),
-) -> Result(a, DecodeError) {
+) -> Result(t, DecodeErrors) {
case decoders {
- [] -> {
- let expected =
- failed
- |> list.intersperse(" or ")
- |> string_builder.from_strings
- |> string_builder.to_string
- Error(DecodeError(found: classify(data), expected: expected))
- }
+ [] ->
+ Error([
+ DecodeError(found: classify(data), expected: "another type", path: []),
+ ])
[decoder, ..decoders] ->
case decoder(data) {
Ok(decoded) -> Ok(decoded)
- Error(DecodeError(expected: expected, ..)) ->
- do_any(data, decoders, [expected, ..failed])
+ Error(_) -> any(data, decoders)
}
}
}
diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam
index 5980d0f..54d9c81 100644
--- a/src/gleam/list.gleam
+++ b/src/gleam/list.gleam
@@ -175,7 +175,7 @@ pub fn contains(list: List(a), any elem: a) -> Bool {
pub fn first(list: List(a)) -> Result(a, Nil) {
case list {
[] -> Error(Nil)
- [x, .._] -> Ok(x)
+ [x, ..] -> Ok(x)
}
}
@@ -1604,7 +1604,7 @@ pub fn transpose(list_of_list: List(List(a))) -> List(List(a)) {
case list {
[] -> []
[f] -> [f]
- [f, .._rest] -> [f]
+ [f, ..] -> [f]
}
}
diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl
index 9a27c18..d88b964 100644
--- a/src/gleam_stdlib.erl
+++ b/src/gleam_stdlib.erl
@@ -50,7 +50,7 @@ iodata_append(Iodata, String) -> [Iodata, String].
identity(X) -> X.
decode_error_msg(Expected, Data) ->
- {error, {decode_error, Expected, classify_dynamic(Data)}}.
+ {error, [{decode_error, Expected, classify_dynamic(Data), []}]}.
classify_dynamic(X) when is_atom(X) -> <<"Atom">>;
classify_dynamic(X) when is_binary(X) -> <<"String">>;
diff --git a/src/gleam_stdlib.mjs b/src/gleam_stdlib.mjs
index 96fa4f1..4d8f65e 100644
--- a/src/gleam_stdlib.mjs
+++ b/src/gleam_stdlib.mjs
@@ -494,7 +494,9 @@ export function classify_dynamic(data) {
}
function decoder_error(expected, got) {
- return new Error(new DecodeError(expected, classify_dynamic(got)));
+ return new Error(
+ new DecodeError(expected, classify_dynamic(got), List.fromArray([]))
+ );
}
export function decode_string(data) {
diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam
index 2c8c96e..f6622aa 100644
--- a/test/gleam/dynamic_test.gleam
+++ b/test/gleam/dynamic_test.gleam
@@ -19,12 +19,16 @@ pub fn bit_string_test() {
1
|> dynamic.from
|> dynamic.bit_string
- |> should.equal(Error(DecodeError(expected: "BitString", found: "Int")))
+ |> should.equal(Error([
+ DecodeError(expected: "BitString", found: "Int", path: []),
+ ]))
[]
|> dynamic.from
|> dynamic.bit_string
- |> should.equal(Error(DecodeError(expected: "BitString", found: "List")))
+ |> should.equal(Error([
+ DecodeError(expected: "BitString", found: "List", path: []),
+ ]))
}
if erlang {
@@ -50,12 +54,16 @@ pub fn string_test() {
1
|> dynamic.from
|> dynamic.string
- |> should.equal(Error(DecodeError(expected: "String", found: "Int")))
+ |> should.equal(Error([
+ DecodeError(expected: "String", found: "Int", path: []),
+ ]))
[]
|> dynamic.from
|> dynamic.string
- |> should.equal(Error(DecodeError(expected: "String", found: "List")))
+ |> should.equal(Error([
+ DecodeError(expected: "String", found: "List", path: []),
+ ]))
}
if erlang {
@@ -63,7 +71,9 @@ if erlang {
<<65535:16>>
|> dynamic.from
|> dynamic.string
- |> should.equal(Error(DecodeError(expected: "String", found: "BitString")))
+ |> should.equal(Error([
+ DecodeError(expected: "String", found: "BitString", path: []),
+ ]))
}
}
@@ -81,7 +91,7 @@ pub fn int_test() {
[]
|> dynamic.from
|> dynamic.int
- |> should.equal(Error(DecodeError(expected: "Int", found: "List")))
+ |> should.equal(Error([DecodeError(expected: "Int", found: "List", path: [])]))
}
pub fn float_test() {
@@ -98,7 +108,9 @@ pub fn float_test() {
[]
|> dynamic.from
|> dynamic.float
- |> should.equal(Error(DecodeError(expected: "Float", found: "List")))
+ |> should.equal(Error([
+ DecodeError(expected: "Float", found: "List", path: []),
+ ]))
}
if erlang {
@@ -106,12 +118,16 @@ if erlang {
1
|> dynamic.from
|> dynamic.float
- |> should.equal(Error(DecodeError(expected: "Float", found: "Int")))
+ |> should.equal(Error([
+ DecodeError(expected: "Float", found: "Int", path: []),
+ ]))
1.0
|> dynamic.from
|> dynamic.int
- |> should.equal(Error(DecodeError(expected: "Int", found: "Float")))
+ |> should.equal(Error([
+ DecodeError(expected: "Int", found: "Float", path: []),
+ ]))
}
}
@@ -143,17 +159,19 @@ pub fn bool_test() {
1
|> dynamic.from
|> dynamic.bool
- |> should.equal(Error(DecodeError(expected: "Bool", found: "Int")))
+ |> should.equal(Error([DecodeError(expected: "Bool", found: "Int", path: [])]))
1.5
|> dynamic.from
|> dynamic.bool
- |> should.equal(Error(DecodeError(expected: "Bool", found: "Float")))
+ |> should.equal(Error([
+ DecodeError(expected: "Bool", found: "Float", path: []),
+ ]))
[]
|> dynamic.from
|> dynamic.bool
- |> should.equal(Error(DecodeError(expected: "Bool", found: "List")))
+ |> should.equal(Error([DecodeError(expected: "Bool", found: "List", path: [])]))
}
pub fn list_test() {
@@ -235,10 +253,9 @@ if javascript {
Ok(123)
|> dynamic.from
|> dynamic.field("Nope", dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Value with field \"Nope\"",
- found: "Result",
- )))
+ |> should.equal(Error([
+ DecodeError(expected: "Value with field \"Nope\"", found: "Result"),
+ ]))
}
}
@@ -265,7 +282,7 @@ pub fn field_test() {
map.new()
|> map.insert("ok", 3)
|> dynamic.from
- |> dynamic.field("ok", dynamic.float)
+ |> dynamic.field("ok", dynamic.string)
|> should.be_error
map.new()
@@ -300,15 +317,20 @@ pub fn element_test() {
ok_one_tuple
|> dynamic.from
|> dynamic.element(1, dynamic.string)
- |> should.equal(Error(DecodeError(expected: "String", found: "Int")))
+ |> should.equal(Error([
+ DecodeError(expected: "String", found: "Int", path: []),
+ ]))
ok_one_tuple
|> dynamic.from
|> dynamic.element(2, dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of at least 3 elements",
- found: "Tuple of 2 elements",
- )))
+ |> should.equal(Error([
+ DecodeError(
+ path: [],
+ expected: "Tuple of at least 3 elements",
+ found: "Tuple of 2 elements",
+ ),
+ ]))
ok_one_tuple
|> dynamic.from
@@ -318,26 +340,29 @@ pub fn element_test() {
ok_one_tuple
|> dynamic.from
|> dynamic.element(-3, dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of at least 3 elements",
- found: "Tuple of 2 elements",
- )))
+ |> should.equal(Error([
+ DecodeError(
+ path: [],
+ expected: "Tuple of at least 3 elements",
+ found: "Tuple of 2 elements",
+ ),
+ ]))
1
|> dynamic.from
|> dynamic.element(-3, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "Tuple", found: "Int")))
+ |> should.equal(Error([DecodeError(expected: "Tuple", found: "Int", path: [])]))
1
|> dynamic.from
|> dynamic.element(0, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "Tuple", found: "Int")))
+ |> should.equal(Error([DecodeError(expected: "Tuple", found: "Int", path: [])]))
map.new()
|> map.insert(1, "ok")
|> dynamic.from
|> dynamic.element(0, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "Tuple", found: "Map")))
+ |> should.equal(Error([DecodeError(expected: "Tuple", found: "Map", path: [])]))
}
pub fn tuple2_test() {
@@ -354,23 +379,27 @@ pub fn tuple2_test() {
#(1, "")
|> dynamic.from
|> dynamic.tuple2(dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "Int", found: "String")))
+ |> should.equal(Error([
+ DecodeError(expected: "Int", found: "String", path: []),
+ ]))
#(1, 2, 3)
|> dynamic.from
|> dynamic.tuple2(dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 2 elements",
- found: "Tuple of 3 elements",
- )))
+ |> should.equal(Error([
+ DecodeError(
+ path: [],
+ expected: "Tuple of 2 elements",
+ found: "Tuple of 3 elements",
+ ),
+ ]))
1
|> dynamic.from
|> dynamic.tuple2(dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 2 elements",
- found: "Int",
- )))
+ |> should.equal(Error([
+ DecodeError(path: [], expected: "Tuple of 2 elements", found: "Int"),
+ ]))
}
pub fn tuple3_test() {
@@ -387,23 +416,27 @@ pub fn tuple3_test() {
#(1, 2, "")
|> dynamic.from
|> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "Int", found: "String")))
+ |> should.equal(Error([
+ DecodeError(expected: "Int", found: "String", path: []),
+ ]))
#(1, 2)
|> dynamic.from
|> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 3 elements",
- found: "Tuple of 2 elements",
- )))
+ |> should.equal(Error([
+ DecodeError(
+ path: [],
+ expected: "Tuple of 3 elements",
+ found: "Tuple of 2 elements",
+ ),
+ ]))
1
|> dynamic.from
|> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 3 elements",
- found: "Int",
- )))
+ |> should.equal(Error([
+ DecodeError(path: [], expected: "Tuple of 3 elements", found: "Int"),
+ ]))
}
pub fn tuple4_test() {
@@ -420,23 +453,27 @@ pub fn tuple4_test() {
#(1, 2, 3, "")
|> dynamic.from
|> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(expected: "Int", found: "String")))
+ |> should.equal(Error([
+ DecodeError(expected: "Int", found: "String", path: []),
+ ]))
#(1, 2)
|> dynamic.from
|> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 4 elements",
- found: "Tuple of 2 elements",
- )))
+ |> should.equal(Error([
+ DecodeError(
+ path: [],
+ expected: "Tuple of 4 elements",
+ found: "Tuple of 2 elements",
+ ),
+ ]))
1
|> dynamic.from
|> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 4 elements",
- found: "Int",
- )))
+ |> should.equal(Error([
+ DecodeError(path: [], expected: "Tuple of 4 elements", found: "Int"),
+ ]))
}
pub fn tuple5_test() {
@@ -471,7 +508,9 @@ pub fn tuple5_test() {
dynamic.int,
dynamic.int,
)
- |> should.equal(Error(DecodeError(expected: "Int", found: "String")))
+ |> should.equal(Error([
+ DecodeError(expected: "Int", found: "String", path: []),
+ ]))
#(1, 2)
|> dynamic.from
@@ -482,10 +521,13 @@ pub fn tuple5_test() {
dynamic.int,
dynamic.int,
)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 5 elements",
- found: "Tuple of 2 elements",
- )))
+ |> should.equal(Error([
+ DecodeError(
+ path: [],
+ expected: "Tuple of 5 elements",
+ found: "Tuple of 2 elements",
+ ),
+ ]))
1
|> dynamic.from
@@ -496,10 +538,9 @@ pub fn tuple5_test() {
dynamic.int,
dynamic.int,
)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 5 elements",
- found: "Int",
- )))
+ |> should.equal(Error([
+ DecodeError(path: [], expected: "Tuple of 5 elements", found: "Int"),
+ ]))
}
pub fn tuple6_test() {
@@ -537,7 +578,9 @@ pub fn tuple6_test() {
dynamic.int,
dynamic.int,
)
- |> should.equal(Error(DecodeError(expected: "Int", found: "String")))
+ |> should.equal(Error([
+ DecodeError(expected: "Int", found: "String", path: []),
+ ]))
#(1, 2)
|> dynamic.from
@@ -549,10 +592,13 @@ pub fn tuple6_test() {
dynamic.int,
dynamic.int,
)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 6 elements",
- found: "Tuple of 2 elements",
- )))
+ |> should.equal(Error([
+ DecodeError(
+ path: [],
+ expected: "Tuple of 6 elements",
+ found: "Tuple of 2 elements",
+ ),
+ ]))
1
|> dynamic.from
@@ -564,10 +610,9 @@ pub fn tuple6_test() {
dynamic.int,
dynamic.int,
)
- |> should.equal(Error(DecodeError(
- expected: "Tuple of 6 elements",
- found: "Int",
- )))
+ |> should.equal(Error([
+ DecodeError(path: [], expected: "Tuple of 6 elements", found: "Int"),
+ ]))
}
pub fn map_test() {
@@ -579,7 +624,7 @@ pub fn map_test() {
1
|> dynamic.from
|> dynamic.map
- |> should.equal(Error(DecodeError(expected: "Map", found: "Int")))
+ |> should.equal(Error([DecodeError(expected: "Map", found: "Int", path: [])]))
}
pub fn shallow_list_test() {
@@ -601,7 +646,7 @@ pub fn shallow_list_test() {
1
|> dynamic.from
|> dynamic.shallow_list
- |> should.equal(Error(DecodeError(expected: "List", found: "Int")))
+ |> should.equal(Error([DecodeError(expected: "List", found: "Int", path: [])]))
}
pub fn result_test() {
@@ -618,17 +663,23 @@ pub fn result_test() {
Ok("1")
|> dynamic.from
|> dynamic.result(ok: dynamic.int, error: dynamic.string)
- |> should.equal(Error(DecodeError(expected: "Int", found: "String")))
+ |> should.equal(Error([
+ DecodeError(expected: "Int", found: "String", path: []),
+ ]))
Error(1)
|> dynamic.from
|> dynamic.result(ok: dynamic.int, error: dynamic.string)
- |> should.equal(Error(DecodeError(expected: "String", found: "Int")))
+ |> should.equal(Error([
+ DecodeError(expected: "String", found: "Int", path: []),
+ ]))
1
|> dynamic.from
|> dynamic.result(ok: dynamic.int, error: dynamic.string)
- |> should.equal(Error(DecodeError(expected: "Result", found: "Int")))
+ |> should.equal(Error([
+ DecodeError(expected: "Result", found: "Int", path: []),
+ ]))
}
pub fn any_test() {
@@ -653,5 +704,5 @@ pub fn any_test() {
""
|> dynamic.from
|> decoder
- |> should.equal(Error(DecodeError("Float or Int", "String")))
+ |> should.equal(Error([DecodeError("another type", "String", path: [])]))
}