diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam index 4fa68eba..870799a4 100644 --- a/src/gleam/dynamic.gleam +++ b/src/gleam/dynamic.gleam @@ -1,9 +1,9 @@ -import gleam/bit_array +import gleam/bit_array as bit_array_mod import gleam/dict.{type Dict} -import gleam/int -import gleam/list +import gleam/int as int_mod +import gleam/list as list_mod import gleam/option.{type Option, Some} -import gleam/result +import gleam/result as result_mod import gleam/string_tree /// `Dynamic` data is data that we don't know the type of yet. @@ -36,6 +36,7 @@ pub fn from(a: anything) -> Dynamic /// when you need to give a decoder function but you don't actually care what /// the to-decode value is. /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn dynamic(value: Dynamic) -> Result(Dynamic, List(DecodeError)) { Ok(value) } @@ -57,6 +58,7 @@ pub fn dynamic(value: Dynamic) -> Result(Dynamic, List(DecodeError)) { /// // -> Error([DecodeError(expected: "BitArray", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn bit_array(from data: Dynamic) -> Result(BitArray, DecodeErrors) { decode_bit_array(data) } @@ -80,12 +82,17 @@ fn decode_bit_array(a: Dynamic) -> Result(BitArray, DecodeErrors) /// // -> Error([DecodeError(expected: "String", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") @external(javascript, "../gleam_stdlib.mjs", "decode_string") pub fn string(from data: Dynamic) -> Result(String, DecodeErrors) { - bit_array(data) + decode_string(data) +} + +fn decode_string(from data: Dynamic) -> Result(String, DecodeErrors) { + decode_bit_array(data) |> map_errors(put_expected(_, "String")) - |> result.try(fn(raw) { - case bit_array.to_string(raw) { + |> result_mod.try(fn(raw) { + case bit_array_mod.to_string(raw) { Ok(string) -> Ok(string) Error(Nil) -> Error([DecodeError(expected: "String", found: "BitArray", path: [])]) @@ -97,7 +104,7 @@ fn map_errors( result: Result(a, DecodeErrors), f: fn(DecodeError) -> DecodeError, ) -> Result(a, DecodeErrors) { - result.map_error(result, list.map(_, f)) + result_mod.map_error(result, list_mod.map(_, f)) } fn put_expected(error: DecodeError, expected: String) -> DecodeError { @@ -130,6 +137,7 @@ pub fn classify(data: Dynamic) -> String /// // -> Error([DecodeError(expected: "Int", found: "String", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn int(from data: Dynamic) -> Result(Int, DecodeErrors) { decode_int(data) } @@ -153,6 +161,7 @@ fn decode_int(a: Dynamic) -> Result(Int, DecodeErrors) /// // -> Error([DecodeError(expected: "Float", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn float(from data: Dynamic) -> Result(Float, DecodeErrors) { decode_float(data) } @@ -176,6 +185,7 @@ fn decode_float(a: Dynamic) -> Result(Float, DecodeErrors) /// // -> Error([DecodeError(expected: "Bool", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn bool(from data: Dynamic) -> Result(Bool, DecodeErrors) { decode_bool(data) } @@ -202,6 +212,7 @@ fn decode_bool(a: Dynamic) -> Result(Bool, DecodeErrors) /// // -> Error([DecodeError(expected: "List", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn shallow_list(from value: Dynamic) -> Result(List(Dynamic), DecodeErrors) { decode_list(value) } @@ -233,23 +244,31 @@ fn decode_list(a: Dynamic) -> Result(List(Dynamic), DecodeErrors) /// // -> Error([DecodeError(expected: "Result", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn result( ok decode_ok: Decoder(a), error decode_error: Decoder(e), +) -> Decoder(Result(a, e)) { + decode_result_please(decode_ok, decode_error) +} + +fn decode_result_please( + ok decode_ok: Decoder(a), + error decode_error: Decoder(e), ) -> Decoder(Result(a, e)) { fn(value) { - use inner_result <- result.try(decode_result(value)) + use inner_result <- result_mod.try(decode_result(value)) case inner_result { Ok(raw) -> { - use value <- result.try( + use value <- result_mod.try( decode_ok(raw) |> map_errors(push_path(_, "ok")), ) Ok(Ok(value)) } Error(raw) -> { - use value <- result.try( + use value <- result_mod.try( decode_error(raw) |> map_errors(push_path(_, "error")), ) @@ -267,7 +286,7 @@ fn decode_result(a: Dynamic) -> Result(Result(a, e), DecodeErrors) /// returns that list if it is. /// /// The second argument is a decoder function used to decode the elements of -/// the list. The list is only decoded if all elements in the list can be +/// the list_mod. The list is only decoded if all elements in the list can be /// successfully decoded using this function. /// /// If you do not wish to decode all the elements in the list use the `shallow_list` @@ -290,13 +309,14 @@ fn decode_result(a: Dynamic) -> Result(Result(a, e), DecodeErrors) /// // -> Error([DecodeError(expected: "List", found: "String", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn list( of decoder_type: fn(Dynamic) -> Result(inner, DecodeErrors), ) -> Decoder(List(inner)) { fn(dynamic) { - use list <- result.try(shallow_list(dynamic)) + use list <- result_mod.try(decode_list(dynamic)) list - |> list.try_map(decoder_type) + |> list_mod.try_map(decoder_type) |> map_errors(push_path(_, "*")) } } @@ -345,6 +365,7 @@ pub fn list( /// // -> Error([DecodeError(expected: "String", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn optional(of decode: Decoder(inner)) -> Decoder(Option(inner)) { fn(value) { decode_optional(value, decode) } } @@ -374,15 +395,16 @@ fn decode_optional(a: Dynamic, b: Decoder(a)) -> Result(Option(a), DecodeErrors) /// // -> Error([DecodeError(expected: "Map", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn field(named name: a, of inner_type: Decoder(t)) -> Decoder(t) { fn(value) { let missing_field_error = DecodeError(expected: "field", found: "nothing", path: []) - use maybe_inner <- result.try(decode_field(value, name)) + use maybe_inner <- result_mod.try(decode_field(value, name)) maybe_inner |> option.to_result([missing_field_error]) - |> result.try(inner_type) + |> result_mod.try(inner_type) |> map_errors(push_path(_, name)) } } @@ -416,17 +438,18 @@ pub fn field(named name: a, of inner_type: Decoder(t)) -> Decoder(t) { /// // -> Error([DecodeError(expected: "Map", found: "Int", path: [])]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn optional_field( named name: a, of inner_type: Decoder(t), ) -> Decoder(Option(t)) { fn(value) { - use maybe_inner <- result.try(decode_field(value, name)) + use maybe_inner <- result_mod.try(decode_field(value, name)) case maybe_inner { option.None -> Ok(option.None) option.Some(dynamic_inner) -> inner_type(dynamic_inner) - |> result.map(Some) + |> result_mod.map(Some) |> map_errors(push_path(_, name)) } } @@ -459,20 +482,22 @@ fn decode_field(a: Dynamic, b: name) -> Result(Option(Dynamic), DecodeErrors) /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn element(at index: Int, of inner_type: Decoder(inner)) -> Decoder(inner) { fn(data: Dynamic) { - use tuple <- result.try(decode_tuple(data)) + use tuple <- result_mod.try(decode_tuple(data)) let size = tuple_size(tuple) - use data <- result.try(case index >= 0 { + use data <- result_mod.try(case index >= 0 { True -> case index < size { True -> tuple_get(tuple, index) False -> at_least_decode_tuple_error(index + 1, data) } False -> - case int.absolute_value(index) <= size { + case int_mod.absolute_value(index) <= size { True -> tuple_get(tuple, size + index) - False -> at_least_decode_tuple_error(int.absolute_value(index), data) + False -> + at_least_decode_tuple_error(int_mod.absolute_value(index), data) } }) inner_type(data) @@ -489,7 +514,7 @@ fn at_least_decode_tuple_error( _ -> "s" } let error = - ["Tuple of at least ", int.to_string(size), " element", s] + ["Tuple of at least ", int_mod.to_string(size), " element", s] |> string_tree.from_strings |> string_tree.to_string |> DecodeError(found: classify(data), path: []) @@ -548,13 +573,17 @@ fn tuple_errors( ) -> List(DecodeError) { case result { Ok(_) -> [] - Error(errors) -> list.map(errors, push_path(_, name)) + Error(errors) -> list_mod.map(errors, push_path(_, name)) } } fn push_path(error: DecodeError, name: t) -> DecodeError { let name = from(name) - let decoder = any([string, fn(x) { result.map(int(x), int.to_string) }]) + let decoder = + any_please([ + decode_string, + fn(x) { result_mod.map(decode_int(x), int_mod.to_string) }, + ]) let name = case decoder(name) { Ok(name) -> name Error(_) -> @@ -618,17 +647,18 @@ fn push_path(error: DecodeError, name: t) -> DecodeError { /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn tuple2( first decode1: Decoder(a), second decode2: Decoder(b), ) -> Decoder(#(a, b)) { fn(value) { - use #(a, b) <- result.try(decode_tuple2(value)) + use #(a, b) <- result_mod.try(decode_tuple2(value)) case decode1(a), decode2(b) { Ok(a), Ok(b) -> Ok(#(a, b)) a, b -> tuple_errors(a, "0") - |> list.append(tuple_errors(b, "1")) + |> list_mod.append(tuple_errors(b, "1")) |> Error } } @@ -687,19 +717,20 @@ pub fn tuple2( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn tuple3( first decode1: Decoder(a), second decode2: Decoder(b), third decode3: Decoder(c), ) -> Decoder(#(a, b, c)) { fn(value) { - use #(a, b, c) <- result.try(decode_tuple3(value)) + use #(a, b, c) <- result_mod.try(decode_tuple3(value)) case decode1(a), decode2(b), decode3(c) { Ok(a), Ok(b), Ok(c) -> Ok(#(a, b, c)) a, b, c -> tuple_errors(a, "0") - |> list.append(tuple_errors(b, "1")) - |> list.append(tuple_errors(c, "2")) + |> list_mod.append(tuple_errors(b, "1")) + |> list_mod.append(tuple_errors(c, "2")) |> Error } } @@ -758,6 +789,7 @@ pub fn tuple3( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn tuple4( first decode1: Decoder(a), second decode2: Decoder(b), @@ -765,14 +797,14 @@ pub fn tuple4( fourth decode4: Decoder(d), ) -> Decoder(#(a, b, c, d)) { fn(value) { - use #(a, b, c, d) <- result.try(decode_tuple4(value)) + use #(a, b, c, d) <- result_mod.try(decode_tuple4(value)) case decode1(a), decode2(b), decode3(c), decode4(d) { Ok(a), Ok(b), Ok(c), Ok(d) -> Ok(#(a, b, c, d)) a, b, c, d -> tuple_errors(a, "0") - |> list.append(tuple_errors(b, "1")) - |> list.append(tuple_errors(c, "2")) - |> list.append(tuple_errors(d, "3")) + |> list_mod.append(tuple_errors(b, "1")) + |> list_mod.append(tuple_errors(c, "2")) + |> list_mod.append(tuple_errors(d, "3")) |> Error } } @@ -831,6 +863,7 @@ pub fn tuple4( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn tuple5( first decode1: Decoder(a), second decode2: Decoder(b), @@ -839,15 +872,15 @@ pub fn tuple5( fifth decode5: Decoder(e), ) -> Decoder(#(a, b, c, d, e)) { fn(value) { - use #(a, b, c, d, e) <- result.try(decode_tuple5(value)) + use #(a, b, c, d, e) <- result_mod.try(decode_tuple5(value)) case decode1(a), decode2(b), decode3(c), decode4(d), decode5(e) { Ok(a), Ok(b), Ok(c), Ok(d), Ok(e) -> Ok(#(a, b, c, d, e)) a, b, c, d, e -> tuple_errors(a, "0") - |> list.append(tuple_errors(b, "1")) - |> list.append(tuple_errors(c, "2")) - |> list.append(tuple_errors(d, "3")) - |> list.append(tuple_errors(e, "4")) + |> list_mod.append(tuple_errors(b, "1")) + |> list_mod.append(tuple_errors(c, "2")) + |> list_mod.append(tuple_errors(d, "3")) + |> list_mod.append(tuple_errors(e, "4")) |> Error } } @@ -906,6 +939,7 @@ pub fn tuple5( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn tuple6( first decode1: Decoder(a), second decode2: Decoder(b), @@ -915,7 +949,7 @@ pub fn tuple6( sixth decode6: Decoder(f), ) -> Decoder(#(a, b, c, d, e, f)) { fn(value) { - use #(a, b, c, d, e, f) <- result.try(decode_tuple6(value)) + use #(a, b, c, d, e, f) <- result_mod.try(decode_tuple6(value)) case decode1(a), decode2(b), @@ -927,11 +961,11 @@ pub fn tuple6( Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f) -> Ok(#(a, b, c, d, e, f)) a, b, c, d, e, f -> tuple_errors(a, "0") - |> list.append(tuple_errors(b, "1")) - |> list.append(tuple_errors(c, "2")) - |> list.append(tuple_errors(d, "3")) - |> list.append(tuple_errors(e, "4")) - |> list.append(tuple_errors(f, "5")) + |> list_mod.append(tuple_errors(b, "1")) + |> list_mod.append(tuple_errors(c, "2")) + |> list_mod.append(tuple_errors(d, "3")) + |> list_mod.append(tuple_errors(e, "4")) + |> list_mod.append(tuple_errors(f, "5")) |> Error } } @@ -957,22 +991,23 @@ pub fn tuple6( /// // -> Error(DecodeError(expected: "Map", found: "String", path: [])) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn dict( of key_type: Decoder(k), to value_type: Decoder(v), ) -> Decoder(Dict(k, v)) { fn(value) { - use dict <- result.try(decode_dict(value)) - use pairs <- result.try( + use dict <- result_mod.try(decode_dict(value)) + use pairs <- result_mod.try( dict |> dict.to_list - |> list.try_map(fn(pair) { + |> list_mod.try_map(fn(pair) { let #(k, v) = pair - use k <- result.try( + use k <- result_mod.try( key_type(k) |> map_errors(push_path(_, "keys")), ) - use v <- result.try( + use v <- result_mod.try( value_type(v) |> map_errors(push_path(_, "values")), ) @@ -1010,7 +1045,12 @@ fn decode_dict(a: Dynamic) -> Result(Dict(Dynamic, Dynamic), DecodeErrors) /// // -> Error(DecodeError(expected: "another type", found: "Int", path: [])) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn any(of decoders: List(Decoder(a))) -> Decoder(a) { + any_please(decoders) +} + +fn any_please(of decoders: List(Decoder(a))) -> Decoder(a) { fn(data) { case decoders { [] -> @@ -1021,7 +1061,7 @@ pub fn any(of decoders: List(Decoder(a))) -> Decoder(a) { [decoder, ..decoders] -> case decoder(data) { Ok(decoded) -> Ok(decoded) - Error(_) -> any(decoders)(data) + Error(_) -> any_please(decoders)(data) } } } @@ -1043,6 +1083,7 @@ pub fn any(of decoders: List(Decoder(a))) -> Decoder(a) { /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode1(constructor: fn(t1) -> t, t1: Decoder(t1)) -> Decoder(t) { fn(value) { case t1(value) { @@ -1071,6 +1112,7 @@ pub fn decode1(constructor: fn(t1) -> t, t1: Decoder(t1)) -> Decoder(t) { /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode2( constructor: fn(t1, t2) -> t, t1: Decoder(t1), @@ -1079,7 +1121,7 @@ pub fn decode2( fn(value) { case t1(value), t2(value) { Ok(a), Ok(b) -> Ok(constructor(a, b)) - a, b -> Error(list.flatten([all_errors(a), all_errors(b)])) + a, b -> Error(list_mod.flatten([all_errors(a), all_errors(b)])) } } } @@ -1103,6 +1145,7 @@ pub fn decode2( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode3( constructor: fn(t1, t2, t3) -> t, t1: Decoder(t1), @@ -1113,7 +1156,7 @@ pub fn decode3( case t1(value), t2(value), t3(value) { Ok(a), Ok(b), Ok(c) -> Ok(constructor(a, b, c)) a, b, c -> - Error(list.flatten([all_errors(a), all_errors(b), all_errors(c)])) + Error(list_mod.flatten([all_errors(a), all_errors(b), all_errors(c)])) } } } @@ -1149,6 +1192,7 @@ pub fn decode3( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode4( constructor: fn(t1, t2, t3, t4) -> t, t1: Decoder(t1), @@ -1161,7 +1205,7 @@ pub fn decode4( Ok(a), Ok(b), Ok(c), Ok(d) -> Ok(constructor(a, b, c, d)) a, b, c, d -> Error( - list.flatten([ + list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), @@ -1205,6 +1249,7 @@ pub fn decode4( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode5( constructor: fn(t1, t2, t3, t4, t5) -> t, t1: Decoder(t1), @@ -1218,7 +1263,7 @@ pub fn decode5( Ok(a), Ok(b), Ok(c), Ok(d), Ok(e) -> Ok(constructor(a, b, c, d, e)) a, b, c, d, e -> Error( - list.flatten([ + list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), @@ -1265,6 +1310,7 @@ pub fn decode5( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode6( constructor: fn(t1, t2, t3, t4, t5, t6) -> t, t1: Decoder(t1), @@ -1280,7 +1326,7 @@ pub fn decode6( Ok(constructor(a, b, c, d, e, f)) a, b, c, d, e, f -> Error( - list.flatten([ + list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), @@ -1330,6 +1376,7 @@ pub fn decode6( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode7( constructor: fn(t1, t2, t3, t4, t5, t6, t7) -> t, t1: Decoder(t1), @@ -1346,7 +1393,7 @@ pub fn decode7( Ok(constructor(a, b, c, d, e, f, g)) a, b, c, d, e, f, g -> Error( - list.flatten([ + list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), @@ -1399,6 +1446,7 @@ pub fn decode7( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode8( constructor: fn(t1, t2, t3, t4, t5, t6, t7, t8) -> t, t1: Decoder(t1), @@ -1416,7 +1464,7 @@ pub fn decode8( Ok(constructor(a, b, c, d, e, f, g, h)) a, b, c, d, e, f, g, h -> Error( - list.flatten([ + list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), @@ -1472,6 +1520,7 @@ pub fn decode8( /// // ]) /// ``` /// +@deprecated("Please use the gleam/dynamic/decode module instead") pub fn decode9( constructor: fn(t1, t2, t3, t4, t5, t6, t7, t8, t9) -> t, t1: Decoder(t1), @@ -1490,7 +1539,7 @@ pub fn decode9( Ok(constructor(a, b, c, d, e, f, g, h, i)) a, b, c, d, e, f, g, h, i -> Error( - list.flatten([ + list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), diff --git a/src/gleam/dynamic/decode.gleam b/src/gleam/dynamic/decode.gleam index e5febceb..33285d66 100644 --- a/src/gleam/dynamic/decode.gleam +++ b/src/gleam/dynamic/decode.gleam @@ -256,6 +256,7 @@ //// decode.run(data, decoder) //// ``` +import gleam/bit_array import gleam/dict.{type Dict} import gleam/dynamic import gleam/int @@ -431,15 +432,12 @@ fn push_path( layer: #(t, List(DecodeError)), path: List(key), ) -> #(t, List(DecodeError)) { - let decoder = - dynamic.any([ - dynamic.string, - fn(x) { result.map(dynamic.int(x), int.to_string) }, - ]) + let decoder = one_of(string, [int |> map(int.to_string)]) + let path = list.map(path, fn(key) { let key = dynamic.from(key) - case decoder(key) { + case run(key, decoder) { Ok(key) -> key Error(_) -> "<" <> dynamic.classify(key) <> ">" } @@ -602,15 +600,16 @@ pub fn optionally_at( fn run_dynamic_function( data: Dynamic, + expected: String, zero: t, - f: dynamic.Decoder(t), + f: fn(Dynamic) -> Result(t, Nil), ) -> #(t, List(DecodeError)) { case f(data) { Ok(data) -> #(data, []) - Error(errors) -> { - let errors = - list.map(errors, fn(e) { DecodeError(e.expected, e.found, e.path) }) - #(zero, errors) + Error(Nil) -> { + let found = dynamic.classify(data) + let error = DecodeError(expected: expected, found: found, path: []) + #(zero, [error]) } } } @@ -627,7 +626,12 @@ fn run_dynamic_function( pub const string: Decoder(String) = Decoder(decode_string) fn decode_string(data: Dynamic) -> #(String, List(DecodeError)) { - run_dynamic_function(data, "", dynamic.string) + run_dynamic_function(data, "String", "", string_decoder_function) +} + +@external(javascript, "../../gleam_stdlib_decode_ffi.mjs", "string") +fn string_decoder_function(data: Dynamic) -> Result(String, Nil) { + bit_array_decoder_function(data) |> result.try(bit_array.to_string) } /// A decoder that decodes `Bool` values. @@ -642,7 +646,17 @@ fn decode_string(data: Dynamic) -> #(String, List(DecodeError)) { pub const bool: Decoder(Bool) = Decoder(decode_bool) fn decode_bool(data: Dynamic) -> #(Bool, List(DecodeError)) { - run_dynamic_function(data, False, dynamic.bool) + let t = dynamic.from(True) + let f = dynamic.from(False) + case Nil { + _ if data == t -> #(True, []) + _ if data == f -> #(False, []) + _ -> { + let error = + DecodeError(expected: "Bool", found: dynamic.classify(data), path: []) + #(False, [error]) + } + } } /// A decoder that decodes `Int` values. @@ -657,9 +671,13 @@ fn decode_bool(data: Dynamic) -> #(Bool, List(DecodeError)) { pub const int: Decoder(Int) = Decoder(decode_int) fn decode_int(data: Dynamic) -> #(Int, List(DecodeError)) { - run_dynamic_function(data, 0, dynamic.int) + run_dynamic_function(data, "Int", 0, int_decoder_function) } +@external(erlang, "gleam_stdlib_decode_ffi", "int") +@external(javascript, "../../gleam_stdlib_decode_ffi.mjs", "int") +fn int_decoder_function(data: Dynamic) -> Result(Int, Nil) + /// A decoder that decodes `Float` values. /// /// # Examples @@ -672,9 +690,13 @@ fn decode_int(data: Dynamic) -> #(Int, List(DecodeError)) { pub const float: Decoder(Float) = Decoder(decode_float) fn decode_float(data: Dynamic) -> #(Float, List(DecodeError)) { - run_dynamic_function(data, 0.0, dynamic.float) + run_dynamic_function(data, "Float", 0.0, float_decoder_function) } +@external(erlang, "gleam_stdlib_decode_ffi", "float") +@external(javascript, "../../gleam_stdlib_decode_ffi.mjs", "float") +fn float_decoder_function(data: Dynamic) -> Result(Float, Nil) + /// A decoder that decodes `Dynamic` values. This decoder never returns an error. /// /// # Examples @@ -702,9 +724,13 @@ fn decode_dynamic(data: Dynamic) -> #(Dynamic, List(DecodeError)) { pub const bit_array: Decoder(BitArray) = Decoder(decode_bit_array) fn decode_bit_array(data: Dynamic) -> #(BitArray, List(DecodeError)) { - run_dynamic_function(data, <<>>, dynamic.bit_array) + run_dynamic_function(data, "BitArray", <<>>, bit_array_decoder_function) } +@external(erlang, "gleam_stdlib_decode_ffi", "bit_array") +@external(javascript, "../../gleam_stdlib_decode_ffi.mjs", "bit_array") +fn bit_array_decoder_function(data: Dynamic) -> Result(BitArray, Nil) + /// A decoder that decodes lists where all elements are decoded with a given /// decoder. /// @@ -816,13 +842,9 @@ fn decode_dict(data: Dynamic) -> Result(Dict(Dynamic, Dynamic), Nil) /// pub fn optional(inner: Decoder(a)) -> Decoder(Option(a)) { Decoder(function: fn(data) { - case dynamic.optional(Ok)(data) { - Ok(option.None) -> #(option.None, []) - Ok(option.Some(data)) -> { - let #(data, errors) = inner.function(data) - #(option.Some(data), errors) - } - Error(_) -> { + case option_decoder_function(data) { + option.None -> #(option.None, []) + option.Some(data) -> { let #(data, errors) = inner.function(data) #(option.Some(data), errors) } @@ -830,6 +852,10 @@ pub fn optional(inner: Decoder(a)) -> Decoder(Option(a)) { }) } +@external(erlang, "gleam_stdlib_decode_ffi", "option") +@external(javascript, "../../gleam_stdlib_decode_ffi.mjs", "option") +fn option_decoder_function(data: Dynamic) -> Option(Dynamic) + /// Apply a transformation function to any value decoded by the decoder. /// /// # Examples diff --git a/src/gleam_stdlib_decode_ffi.erl b/src/gleam_stdlib_decode_ffi.erl index 35af05e7..4262b9c6 100644 --- a/src/gleam_stdlib_decode_ffi.erl +++ b/src/gleam_stdlib_decode_ffi.erl @@ -1,6 +1,8 @@ -module(gleam_stdlib_decode_ffi). --export([strict_index/2, list/5, dict/1]). +-export([ + strict_index/2, list/5, dict/1, bit_array/1, int/1, float/1, option/1 +]). strict_index([X | _], 0) -> {ok, {some, X}}; @@ -42,3 +44,19 @@ list(_, _, _, _, Acc) -> dict(#{} = Data) -> {ok, Data}; dict(_) -> {error, nil}. + +bit_array(B) when is_bitstring(B) -> {ok, B}; +bit_array(_) -> {error, nil}. + +int(X) when is_integer(X) -> {ok, X}; +int(_) -> {error, nil}. + +float(X) when is_float(X) -> {ok, X}; +float(_) -> {error, nil}. + +option(nil) -> none; +option(null) -> none; +option(none) -> none; +option(error) -> none; +option(undefined) -> none; +option(X) -> {some, X}. diff --git a/src/gleam_stdlib_decode_ffi.mjs b/src/gleam_stdlib_decode_ffi.mjs index f31c4642..4dc3d32e 100644 --- a/src/gleam_stdlib_decode_ffi.mjs +++ b/src/gleam_stdlib_decode_ffi.mjs @@ -1,4 +1,4 @@ -import { Ok, Error, List, NonEmpty } from "./gleam.mjs"; +import { Ok, Error, List, NonEmpty, BitArray } from "./gleam.mjs"; import { default as Dict } from "./dict.mjs"; import { Some, None } from "./gleam/option.mjs"; import { classify } from "./gleam/dynamic.mjs"; @@ -80,3 +80,44 @@ export function dict(data) { } return new Error("Dict"); } + +export function bit_array(data) { + if (data instanceof BitArray) { + return new Ok(data); + } + if (data instanceof Uint8Array) { + return new Ok(new BitArray(data)); + } + return new Error(undefined); +} + +export function int(data) { + if (Number.isInteger(data)) { + return new Ok(data); + } + return new Error(undefined); +} + +export function float(data) { + if (typeof data === "number") { + return new Ok(data); + } + return new Error(undefined); +} + +export function string(data) { + if (typeof data === "string") { + return new Ok(data); + } + return new Error(undefined); +} + +export function option(data, decoder) { + if (data === null || data === undefined || data instanceof None) { + return new None(); + } + if (data instanceof Some) { + return data; + } + return new Some(data); +} diff --git a/test/gleam/bit_array_test.gleam b/test/gleam/bit_array_test.gleam index 30e8c17d..958d8ddf 100644 --- a/test/gleam/bit_array_test.gleam +++ b/test/gleam/bit_array_test.gleam @@ -1,9 +1,11 @@ import gleam/bit_array -import gleam/order import gleam/result import gleam/should import gleam/string +@target(erlang) +import gleam/order + pub fn bit_size_test() { bit_array.bit_size(<<>>) |> should.equal(0) diff --git a/test/gleam/dynamic/decode_test.gleam b/test/gleam/dynamic/decode_test.gleam index a97f5973..34cb4b62 100644 --- a/test/gleam/dynamic/decode_test.gleam +++ b/test/gleam/dynamic/decode_test.gleam @@ -768,32 +768,40 @@ pub fn documentation_variants_example_test() { ]) } -pub fn new_primitive_decoder_string_ok_test() { +pub fn new_primitive_decoder_int_ok_test() { let decoder = - decode.new_primitive_decoder("String", fn(x) { - dynamic.string(x) |> result.replace_error("") + decode.new_primitive_decoder("Int", fn(x) { + decode_int(x) |> result.replace_error(0) }) - dynamic.from("Hello!") + dynamic.from(123) |> decode.run(decoder) |> should.be_ok - |> should.equal("Hello!") + |> should.equal(123) } -pub fn new_primitive_decoder_string_error_test() { +@external(erlang, "gleam_stdlib", "decode_int") +@external(javascript, "../../gleam_stdlib.mjs", "decode_int") +fn decode_int(a: Dynamic) -> Result(Int, Dynamic) + +@external(erlang, "gleam_stdlib", "decode_float") +@external(javascript, "../../gleam_stdlib.mjs", "decode_float") +fn decode_float(a: Dynamic) -> Result(Float, Dynamic) + +pub fn new_primitive_decoder_int_error_test() { let decoder = - decode.new_primitive_decoder("String", fn(x) { - dynamic.string(x) |> result.replace_error("") + decode.new_primitive_decoder("Int", fn(x) { + decode_int(x) |> result.replace_error(0) }) - dynamic.from(123) + dynamic.from("hi") |> decode.run(decoder) |> should.be_error - |> should.equal([DecodeError("String", "Int", [])]) + |> should.equal([DecodeError("Int", "String", [])]) } pub fn new_primitive_decoder_float_ok_test() { let decoder = decode.new_primitive_decoder("Float", fn(x) { - dynamic.float(x) |> result.replace_error(0.0) + decode_float(x) |> result.replace_error(0.0) }) dynamic.from(12.4) |> decode.run(decoder) @@ -804,7 +812,7 @@ pub fn new_primitive_decoder_float_ok_test() { pub fn new_primitive_decoder_float_error_test() { let decoder = decode.new_primitive_decoder("Float", fn(x) { - dynamic.float(x) |> result.replace_error(0.0) + decode_float(x) |> result.replace_error(0.0) }) dynamic.from("blah") |> decode.run(decoder) diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam index fd51212c..ae6afa79 100644 --- a/test/gleam/dynamic_test.gleam +++ b/test/gleam/dynamic_test.gleam @@ -1,1543 +1,6 @@ -import gleam/dict -import gleam/dynamic.{DecodeError} -import gleam/option.{None, Some} -import gleam/result +import gleam/dynamic import gleam/should -pub fn bit_array_test() { - <<>> - |> dynamic.from - |> dynamic.bit_array - |> should.equal(Ok(<<>>)) - - <<"Hello":utf8>> - |> dynamic.from - |> dynamic.bit_array - |> should.equal(Ok(<<"Hello":utf8>>)) - - 1 - |> dynamic.from - |> dynamic.bit_array - |> should.equal( - Error([DecodeError(expected: "BitArray", found: "Int", path: [])]), - ) - - [] - |> dynamic.from - |> dynamic.bit_array - |> should.equal( - Error([DecodeError(expected: "BitArray", found: "List", path: [])]), - ) -} - -@target(erlang) -pub fn bit_array_erlang_test() { - <<65_535:16>> - |> dynamic.from - |> dynamic.bit_array - |> should.equal(Ok(<<65_535:16>>)) -} - -@target(javascript) -@external(javascript, "../gleam_stdlib_test_ffi.mjs", "uint8array") -fn uint8array(a: List(Int)) -> dynamic.Dynamic - -@target(javascript) -pub fn bit_array_erlang_test() { - [1, 1, 2, 3, 5, 8] - |> uint8array - |> dynamic.bit_array - |> should.equal(Ok(<<1, 1, 2, 3, 5, 8>>)) -} - -@target(erlang) -pub type MyAtom { - ThisIsAnAtom -} - -@target(erlang) -pub fn dict_from_atom_test() { - ThisIsAnAtom - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Atom", path: [])]), - ) -} - -@target(javascript) -@external(javascript, "../gleam_stdlib_test_ffi.mjs", "get_null") -fn get_null() -> dynamic.Dynamic - -@target(javascript) -pub fn dict_from_null_test() { - get_null() - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Null", path: [])]), - ) -} - -@target(javascript) -pub fn null_for_field_test() { - get_null() - |> dynamic.from - |> dynamic.field("x", dynamic.string) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Null", path: [])]), - ) -} - -pub fn string_test() { - "" - |> dynamic.from - |> dynamic.string - |> should.equal(Ok("")) - - "Hello" - |> dynamic.from - |> dynamic.string - |> should.equal(Ok("Hello")) - - 1 - |> dynamic.from - |> dynamic.string - |> should.equal( - Error([DecodeError(expected: "String", found: "Int", path: [])]), - ) - - [] - |> dynamic.from - |> dynamic.string - |> should.equal( - Error([DecodeError(expected: "String", found: "List", path: [])]), - ) -} - -@target(erlang) -pub fn string_non_utf8_test() { - <<65_535:16>> - |> dynamic.from - |> dynamic.string - |> should.equal( - Error([DecodeError(expected: "String", found: "BitArray", path: [])]), - ) -} - -pub fn int_test() { - 1 - |> dynamic.from - |> dynamic.int - |> should.equal(Ok(1)) - - 2 - |> dynamic.from - |> dynamic.int - |> should.equal(Ok(2)) - - [] - |> dynamic.from - |> dynamic.int - |> should.equal( - Error([DecodeError(expected: "Int", found: "List", path: [])]), - ) -} - -pub fn float_test() { - 1.0 - |> dynamic.from - |> dynamic.float - |> should.equal(Ok(1.0)) - - 2.2 - |> dynamic.from - |> dynamic.float - |> should.equal(Ok(2.2)) - - [] - |> dynamic.from - |> dynamic.float - |> should.equal( - Error([DecodeError(expected: "Float", found: "List", path: [])]), - ) -} - -@target(erlang) -pub fn float_on_js_is_also_int_test() { - 1 - |> dynamic.from - |> dynamic.float - |> should.equal( - Error([DecodeError(expected: "Float", found: "Int", path: [])]), - ) - - 1.0 - |> dynamic.from - |> dynamic.int - |> should.equal( - Error([DecodeError(expected: "Int", found: "Float", path: [])]), - ) -} - -@target(javascript) -pub fn float_on_js_is_also_int_test() { - 1 - |> dynamic.from - |> dynamic.float - |> should.equal(Ok(1.0)) - - 1.0 - |> dynamic.from - |> dynamic.int - |> should.equal(Ok(1)) -} - -pub fn bool_test() { - True - |> dynamic.from - |> dynamic.bool - |> should.equal(Ok(True)) - - False - |> dynamic.from - |> dynamic.bool - |> should.equal(Ok(False)) - - 1 - |> dynamic.from - |> dynamic.bool - |> should.equal( - Error([DecodeError(expected: "Bool", found: "Int", path: [])]), - ) - - 1.5 - |> dynamic.from - |> dynamic.bool - |> should.equal( - Error([DecodeError(expected: "Bool", found: "Float", path: [])]), - ) - - [] - |> dynamic.from - |> dynamic.bool - |> should.equal( - Error([DecodeError(expected: "Bool", found: "List", path: [])]), - ) -} - -pub fn list_test() { - [] - |> dynamic.from - |> dynamic.list(dynamic.string) - |> should.equal(Ok([])) - - [] - |> dynamic.from - |> dynamic.list(dynamic.int) - |> should.equal(Ok([])) - - [1, 2, 3] - |> dynamic.from - |> dynamic.list(dynamic.int) - |> should.equal(Ok([1, 2, 3])) - - [[1], [2], [3]] - |> dynamic.from - |> dynamic.list(dynamic.list(dynamic.int)) - |> should.equal(Ok([[1], [2], [3]])) - - 1 - |> dynamic.from - |> dynamic.list(dynamic.string) - |> should.equal( - Error([DecodeError(expected: "List", found: "Int", path: [])]), - ) - - 1.1 - |> dynamic.from - |> dynamic.list(dynamic.int) - |> should.equal( - Error([DecodeError(expected: "List", found: "Float", path: [])]), - ) - - [""] - |> dynamic.from - |> dynamic.list(dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["*"])]), - ) - - [dynamic.from(1), dynamic.from("not an int")] - |> dynamic.from - |> dynamic.list(dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["*"])]), - ) -} - -pub fn optional_test() { - 1 - |> dynamic.from - |> dynamic.optional(dynamic.int) - |> should.equal(Ok(Some(1))) - - option.None - |> dynamic.from - |> dynamic.optional(dynamic.int) - |> should.equal(Ok(None)) - - Nil - |> dynamic.from - |> dynamic.optional(dynamic.int) - |> should.equal(Ok(None)) - - 1 - |> dynamic.from - |> dynamic.optional(dynamic.string) - |> should.be_error -} - -@target(javascript) -pub fn javascript_object_field_test() { - Ok(123) - |> dynamic.from - |> dynamic.field("0", dynamic.int) - |> should.equal(Ok(123)) - - Ok(123) - |> dynamic.from - |> dynamic.field(0, dynamic.int) - |> should.equal(Ok(123)) - - Ok(123) - |> dynamic.from - |> dynamic.field("Nope", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Result", path: [])]), - ) -} - -pub fn field_test() { - dict.new() - |> dict.insert("ok", 1) - |> dynamic.from - |> dynamic.field(named: "ok", of: dynamic.int) - |> should.equal(Ok(1)) - - dict.new() - |> dict.insert("ok", 1.0) - |> dynamic.from - |> dynamic.field(named: "ok", of: dynamic.float) - |> should.equal(Ok(1.0)) - - dict.new() - |> dict.insert("ok", 3) - |> dict.insert("error", 1) - |> dynamic.from - |> dynamic.field("ok", dynamic.int) - |> should.equal(Ok(3)) - - dict.new() - |> dict.insert("ok", 3) - |> dynamic.from - |> dynamic.field("ok", dynamic.string) - |> should.equal( - Error([DecodeError(expected: "String", found: "Int", path: ["ok"])]), - ) - - dict.new() - |> dynamic.from - |> dynamic.field("ok", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "field", found: "nothing", path: ["ok"])]), - ) - - 1 - |> dynamic.from - |> dynamic.field("ok", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Int", path: [])]), - ) - - [] - |> dynamic.from - |> dynamic.field("ok", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "List", path: [])]), - ) - - dict.new() - |> dict.insert("ok", 1) - |> dynamic.from - |> dynamic.field("ok", dynamic.field("not_a_field", dynamic.int)) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Int", path: ["ok"])]), - ) -} - -pub fn optional_field_test() { - dict.new() - |> dict.insert("ok", 1) - |> dynamic.from - |> dynamic.optional_field(named: "ok", of: dynamic.int) - |> should.equal(Ok(Some(1))) - - dict.new() - |> dict.insert("ok", 1.0) - |> dynamic.from - |> dynamic.optional_field(named: "ok", of: dynamic.float) - |> should.equal(Ok(Some(1.0))) - - dict.new() - |> dict.insert("ok", 3) - |> dict.insert("error", 1) - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.int) - |> should.equal(Ok(Some(3))) - - dict.new() - |> dict.insert("ok", 3) - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.string) - |> should.equal( - Error([DecodeError(expected: "String", found: "Int", path: ["ok"])]), - ) - - dict.new() - |> dict.insert("ok", Nil) - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "Nil", path: ["ok"])]), - ) - - // optional_field and optional should combine together to give nested Options - dict.new() - |> dict.insert("ok", Nil) - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.optional(dynamic.int)) - |> should.equal(Ok(Some(None))) - - dict.new() - |> dict.insert("ok", 4) - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.optional(dynamic.int)) - |> should.equal(Ok(Some(Some(4)))) - - dict.new() - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.int) - |> should.equal(Ok(None)) - - 1 - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Int", path: [])]), - ) - - [] - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "List", path: [])]), - ) -} - -// Error is different for erlang & javascript -@target(javascript) -pub fn optional_field_error_test() { - dict.new() - |> dict.insert("ok", None) - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "Object", path: ["ok"])]), - ) -} - -// Error is different for erlang & javascript -@target(erlang) -pub fn optional_field_error_test() { - dict.new() - |> dict.insert("ok", None) - |> dynamic.from - |> dynamic.optional_field("ok", dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "Atom", path: ["ok"])]), - ) -} - -pub fn element_test() { - let ok_one_tuple = #("ok", 1) - - ok_one_tuple - |> dynamic.from - |> dynamic.element(0, dynamic.string) - |> should.equal(Ok("ok")) - - ok_one_tuple - |> dynamic.from - |> dynamic.element(1, dynamic.int) - |> should.equal(Ok(1)) - - ok_one_tuple - |> dynamic.from - |> dynamic.element(1, dynamic.string) - |> should.equal( - Error([DecodeError(expected: "String", found: "Int", path: ["1"])]), - ) - - ok_one_tuple - |> dynamic.from - |> dynamic.element(2, dynamic.int) - |> should.equal( - Error([ - DecodeError( - path: [], - expected: "Tuple of at least 3 elements", - found: "Tuple of 2 elements", - ), - ]), - ) - - ok_one_tuple - |> dynamic.from - |> dynamic.element(-1, dynamic.int) - |> should.equal(Ok(1)) - - ok_one_tuple - |> dynamic.from - |> dynamic.element(-3, dynamic.int) - |> 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", path: [])]), - ) - - 1 - |> dynamic.from - |> dynamic.element(0, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Tuple", found: "Int", path: [])]), - ) - - dict.new() - |> dict.insert(1, "ok") - |> dynamic.from - |> dynamic.element(0, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Tuple", found: "Dict", path: [])]), - ) -} - -pub fn tuple2_test() { - #(1, 2) - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.int) - |> should.equal(Ok(#(1, 2))) - - #(1, "") - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.string) - |> should.equal(Ok(#(1, ""))) - - #(1, "") - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["1"])]), - ) - - #(1.2, "") - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - ]), - ) - - #(1, 2, 3) - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.int) - |> 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(path: [], expected: "Tuple of 2 elements", found: "Int")]), - ) - - [1, 2] - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.int) - |> should.equal(Ok(#(1, 2))) - - [dynamic.from(1), dynamic.from("a")] - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.string) - |> should.equal(Ok(#(1, "a"))) - - ["", ""] - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "String", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - ]), - ) - - [1, 2, 3] - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(path: [], expected: "Tuple of 2 elements", found: "List"), - ]), - ) - - [] - |> dynamic.from - |> dynamic.tuple2(dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(path: [], expected: "Tuple of 2 elements", found: "List"), - ]), - ) -} - -pub fn tuple3_test() { - #(1, 2, 3) - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int) - |> should.equal(Ok(#(1, 2, 3))) - - #(1, "", 3.0) - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.string, dynamic.float) - |> should.equal(Ok(#(1, "", 3.0))) - - [1, 2, 3] - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int) - |> should.equal(Ok(#(1, 2, 3))) - - [dynamic.from(1), dynamic.from("a"), dynamic.from(3.0)] - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.string, dynamic.float) - |> should.equal(Ok(#(1, "a", 3.0))) - - #(1, 2, "") - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["2"])]), - ) - - #(1.2, "", "") - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - DecodeError(expected: "Int", found: "String", path: ["2"]), - ]), - ) - - #(1, 2) - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError( - path: [], - expected: "Tuple of 3 elements", - found: "Tuple of 2 elements", - ), - ]), - ) - - ["", "", ""] - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "String", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - DecodeError(expected: "Int", found: "String", path: ["2"]), - ]), - ) - - [1, 2] - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(path: [], expected: "Tuple of 3 elements", found: "List"), - ]), - ) - - 1 - |> dynamic.from - |> dynamic.tuple3(dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([DecodeError(path: [], expected: "Tuple of 3 elements", found: "Int")]), - ) -} - -pub fn tuple4_test() { - #(1, 2, 3, 4) - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) - |> should.equal(Ok(#(1, 2, 3, 4))) - - #(1, "", 3.0, 4) - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.string, dynamic.float, dynamic.int) - |> should.equal(Ok(#(1, "", 3.0, 4))) - - [1, 2, 3, 4] - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) - |> should.equal(Ok(#(1, 2, 3, 4))) - - [dynamic.from(1), dynamic.from("a"), dynamic.from(3.0), dynamic.from(4.0)] - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.string, dynamic.float, dynamic.float) - |> should.equal(Ok(#(1, "a", 3.0, 4.0))) - - #(1, 2, 3, "") - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["3"])]), - ) - - #(1.2, "", "", "") - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - DecodeError(expected: "Int", found: "String", path: ["2"]), - DecodeError(expected: "Int", found: "String", path: ["3"]), - ]), - ) - - #(1, 2) - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError( - path: [], - expected: "Tuple of 4 elements", - found: "Tuple of 2 elements", - ), - ]), - ) - - ["", "", "", ""] - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "String", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - DecodeError(expected: "Int", found: "String", path: ["2"]), - DecodeError(expected: "Int", found: "String", path: ["3"]), - ]), - ) - - [1, 2] - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([ - DecodeError(path: [], expected: "Tuple of 4 elements", found: "List"), - ]), - ) - - 1 - |> dynamic.from - |> dynamic.tuple4(dynamic.int, dynamic.int, dynamic.int, dynamic.int) - |> should.equal( - Error([DecodeError(path: [], expected: "Tuple of 4 elements", found: "Int")]), - ) -} - -pub fn tuple5_test() { - #(1, 2, 3, 4, 5) - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal(Ok(#(1, 2, 3, 4, 5))) - - #(1, "", 3.0, 4, 5) - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.string, - dynamic.float, - dynamic.int, - dynamic.int, - ) - |> should.equal(Ok(#(1, "", 3.0, 4, 5))) - - [1, 2, 3, 4, 5] - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal(Ok(#(1, 2, 3, 4, 5))) - - [ - dynamic.from(1), - dynamic.from("a"), - dynamic.from(3.0), - dynamic.from(4.0), - dynamic.from(True), - ] - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.string, - dynamic.float, - dynamic.float, - dynamic.bool, - ) - |> should.equal(Ok(#(1, "a", 3.0, 4.0, True))) - - #(1, 2, 3, 4, "") - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["4"])]), - ) - - #(1.2, "", "", "", "") - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - DecodeError(expected: "Int", found: "String", path: ["2"]), - DecodeError(expected: "Int", found: "String", path: ["3"]), - DecodeError(expected: "Int", found: "String", path: ["4"]), - ]), - ) - - #(1, 2) - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([ - DecodeError( - path: [], - expected: "Tuple of 5 elements", - found: "Tuple of 2 elements", - ), - ]), - ) - - ["", "", "", "", ""] - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "String", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - DecodeError(expected: "Int", found: "String", path: ["2"]), - DecodeError(expected: "Int", found: "String", path: ["3"]), - DecodeError(expected: "Int", found: "String", path: ["4"]), - ]), - ) - - [1, 2] - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([ - DecodeError(path: [], expected: "Tuple of 5 elements", found: "List"), - ]), - ) - - 1 - |> dynamic.from - |> dynamic.tuple5( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([DecodeError(path: [], expected: "Tuple of 5 elements", found: "Int")]), - ) -} - -pub fn tuple6_test() { - #(1, 2, 3, 4, 5, 6) - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal(Ok(#(1, 2, 3, 4, 5, 6))) - - #(1, "", 3.0, 4, 5, 6) - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.string, - dynamic.float, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal(Ok(#(1, "", 3.0, 4, 5, 6))) - - [1, 2, 3, 4, 5, 6] - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal(Ok(#(1, 2, 3, 4, 5, 6))) - - [ - dynamic.from(1), - dynamic.from("a"), - dynamic.from(3.0), - dynamic.from(4.0), - dynamic.from(True), - dynamic.from(6.0), - ] - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.string, - dynamic.float, - dynamic.float, - dynamic.bool, - dynamic.float, - ) - |> should.equal(Ok(#(1, "a", 3.0, 4.0, True, 6.0))) - - #(1, 2, 3, 4, 5, "") - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["5"])]), - ) - - #(1.2, "", "", "", "", "") - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - DecodeError(expected: "Int", found: "String", path: ["2"]), - DecodeError(expected: "Int", found: "String", path: ["3"]), - DecodeError(expected: "Int", found: "String", path: ["4"]), - DecodeError(expected: "Int", found: "String", path: ["5"]), - ]), - ) - - #(1, 2) - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([ - DecodeError( - path: [], - expected: "Tuple of 6 elements", - found: "Tuple of 2 elements", - ), - ]), - ) - - ["", "", "", "", "", ""] - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "String", path: ["0"]), - DecodeError(expected: "Int", found: "String", path: ["1"]), - DecodeError(expected: "Int", found: "String", path: ["2"]), - DecodeError(expected: "Int", found: "String", path: ["3"]), - DecodeError(expected: "Int", found: "String", path: ["4"]), - DecodeError(expected: "Int", found: "String", path: ["5"]), - ]), - ) - - [1, 2] - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([ - DecodeError(path: [], expected: "Tuple of 6 elements", found: "List"), - ]), - ) - - 1 - |> dynamic.from - |> dynamic.tuple6( - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - dynamic.int, - ) - |> should.equal( - Error([DecodeError(path: [], expected: "Tuple of 6 elements", found: "Int")]), - ) -} - -pub fn nested_tuples_test() { - #(1, #(2, #("3", 4))) - |> dynamic.from - |> dynamic.tuple2( - dynamic.int, - dynamic.tuple2(dynamic.int, dynamic.tuple2(dynamic.int, dynamic.int)), - ) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["1", "1", "0"])]), - ) -} - -pub fn dict_test() { - dict.new() - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.int) - |> should.equal(Ok(dict.new())) - - dict.from_list([#("a", 1), #("b", 2)]) - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.int) - |> should.equal(Ok(dict.from_list([#("a", 1), #("b", 2)]))) - - dict.from_list([#("a", 1), #("b", 2)]) - |> dynamic.from - |> dynamic.dict(dynamic.int, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["keys"])]), - ) - - dict.from_list([#("a", 1), #("b", 2)]) - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.string) - |> should.equal( - Error([DecodeError(expected: "String", found: "Int", path: ["values"])]), - ) - - 1 - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Int", path: [])]), - ) - - #() - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.int) - |> should.equal( - Error([ - DecodeError(expected: "Dict", found: "Tuple of 0 elements", path: []), - ]), - ) - - fn() { Nil } - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Function", path: [])]), - ) - - Nil - |> dynamic.from - |> dynamic.dict(dynamic.string, dynamic.int) - |> should.equal( - Error([DecodeError(expected: "Dict", found: "Nil", path: [])]), - ) -} - -pub fn shallow_list_test() { - [] - |> dynamic.from - |> dynamic.shallow_list - |> should.equal(Ok([])) - - [1, 2] - |> dynamic.from - |> dynamic.shallow_list - |> should.equal(Ok([dynamic.from(1), dynamic.from(2)])) - - [dynamic.from(1), dynamic.from(2.0)] - |> dynamic.from - |> dynamic.shallow_list - |> should.equal(Ok([dynamic.from(1), dynamic.from(2.0)])) - - 1 - |> dynamic.from - |> dynamic.shallow_list - |> should.equal( - Error([DecodeError(expected: "List", found: "Int", path: [])]), - ) -} - -@target(javascript) -pub fn array_on_js_is_also_list_test() { - #() - |> dynamic.from - |> dynamic.shallow_list - |> should.equal(Ok([])) - - #(1, 2) - |> dynamic.from - |> dynamic.list(of: dynamic.int) - |> should.equal(Ok([1, 2])) -} - -pub fn result_test() { - Ok(1) - |> dynamic.from - |> dynamic.result(ok: dynamic.int, error: dynamic.string) - |> should.equal(Ok(Ok(1))) - - Error("error") - |> dynamic.from - |> dynamic.result(ok: dynamic.int, error: dynamic.string) - |> should.equal(Ok(Error("error"))) - - Ok("1") - |> dynamic.from - |> dynamic.result(ok: dynamic.int, error: dynamic.string) - |> should.equal( - Error([DecodeError(expected: "Int", found: "String", path: ["ok"])]), - ) - - Error(1) - |> dynamic.from - |> dynamic.result(ok: dynamic.int, error: dynamic.string) - |> should.equal( - Error([DecodeError(expected: "String", found: "Int", path: ["error"])]), - ) - - 1 - |> dynamic.from - |> dynamic.result(ok: dynamic.int, error: dynamic.string) - |> should.equal( - Error([DecodeError(expected: "Result", found: "Int", path: [])]), - ) -} - -pub fn any_test() { - let decoder = - dynamic.any([ - fn(x) { result.map(dynamic.int(x), fn(_) { "int" }) }, - fn(x) { result.map(dynamic.float(x), fn(_) { "float" }) }, - ]) - - 1 - |> dynamic.from - |> decoder - |> should.equal(Ok("int")) - - 1.1 - |> dynamic.from - |> decoder - |> should.equal(Ok("float")) - - "" - |> dynamic.from - |> decoder - |> should.equal(Error([DecodeError("another type", "String", path: [])])) -} - -type One(a) { - One(a) -} - -pub fn decode1_test() { - let decoder = dynamic.decode1(One, dynamic.element(0, dynamic.int)) - - #(1) - |> dynamic.from - |> decoder - |> should.equal(Ok(One(1))) - - #(1.3) - |> dynamic.from - |> decoder - |> should.equal( - Error([DecodeError(expected: "Int", found: "Float", path: ["0"])]), - ) -} - -type Two(a, b) { - Two(a, b) -} - -pub fn decode2_test() { - let decoder = - dynamic.decode2( - Two, - dynamic.element(0, dynamic.int), - dynamic.element(1, dynamic.string), - ) - - #(1, "2") - |> dynamic.from - |> decoder - |> should.equal(Ok(Two(1, "2"))) - - #(1.3, 2) - |> dynamic.from - |> decoder - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "String", found: "Int", path: ["1"]), - ]), - ) -} - -type Three(a, b, c) { - Three(a, b, c) -} - -pub fn decode3_test() { - let decoder = - dynamic.decode3( - Three, - dynamic.element(0, dynamic.int), - dynamic.element(1, dynamic.string), - dynamic.element(2, dynamic.int), - ) - - #(1, "2", 3) - |> dynamic.from - |> decoder - |> should.equal(Ok(Three(1, "2", 3))) - - #(1.3, 2.1, 3) - |> dynamic.from - |> decoder - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "String", found: "Float", path: ["1"]), - ]), - ) -} - -type Four(a, b, c, d) { - Four(a, b, c, d) -} - -pub fn decode4_test() { - let decoder = - dynamic.decode4( - Four, - dynamic.element(0, dynamic.int), - dynamic.element(1, dynamic.string), - dynamic.element(2, dynamic.int), - dynamic.element(3, dynamic.int), - ) - - #(1, "2", 3, 4) - |> dynamic.from - |> decoder - |> should.equal(Ok(Four(1, "2", 3, 4))) - - #(1.3, 2.1, 3, 4) - |> dynamic.from - |> decoder - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "String", found: "Float", path: ["1"]), - ]), - ) -} - -type Five(a, b, c, d, e) { - Five(a, b, c, d, e) -} - -pub fn decode5_test() { - let decoder = - dynamic.decode5( - Five, - dynamic.element(0, dynamic.int), - dynamic.element(1, dynamic.string), - dynamic.element(2, dynamic.int), - dynamic.element(3, dynamic.int), - dynamic.element(4, dynamic.int), - ) - - #(1, "2", 3, 4, 5) - |> dynamic.from - |> decoder - |> should.equal(Ok(Five(1, "2", 3, 4, 5))) - - #(1.3, 2.1, 3, 4, 5) - |> dynamic.from - |> decoder - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "String", found: "Float", path: ["1"]), - ]), - ) -} - -type Six(a, b, c, d, e, f) { - Six(a, b, c, d, e, f) -} - -pub fn decode6_test() { - let decoder = - dynamic.decode6( - Six, - dynamic.element(0, dynamic.int), - dynamic.element(1, dynamic.string), - dynamic.element(2, dynamic.int), - dynamic.element(3, dynamic.int), - dynamic.element(4, dynamic.int), - dynamic.element(5, dynamic.int), - ) - - #(1, "2", 3, 4, 5, 6) - |> dynamic.from - |> decoder - |> should.equal(Ok(Six(1, "2", 3, 4, 5, 6))) - - #(1.3, 2.1, 3, 4, 5, 6) - |> dynamic.from - |> decoder - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "String", found: "Float", path: ["1"]), - ]), - ) -} - -type Seven(a, b, c, d, e, f, g) { - Seven(a, b, c, d, e, f, g) -} - -pub fn decode7_test() { - let decoder = - dynamic.decode7( - Seven, - dynamic.element(0, dynamic.int), - dynamic.element(1, dynamic.string), - dynamic.element(2, dynamic.int), - dynamic.element(3, dynamic.int), - dynamic.element(4, dynamic.int), - dynamic.element(5, dynamic.int), - dynamic.element(6, dynamic.int), - ) - - #(1, "2", 3, 4, 5, 6, 7) - |> dynamic.from - |> decoder - |> should.equal(Ok(Seven(1, "2", 3, 4, 5, 6, 7))) - - #(1.3, 2.1, 3, 4, 5, 6, 7) - |> dynamic.from - |> decoder - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "String", found: "Float", path: ["1"]), - ]), - ) -} - -type Eight(a, b, c, d, e, f, g, h) { - Eight(a, b, c, d, e, f, g, h) -} - -pub fn decode8_test() { - let decoder = - dynamic.decode8( - Eight, - dynamic.element(0, dynamic.int), - dynamic.element(1, dynamic.string), - dynamic.element(2, dynamic.int), - dynamic.element(3, dynamic.int), - dynamic.element(4, dynamic.int), - dynamic.element(5, dynamic.int), - dynamic.element(6, dynamic.int), - dynamic.element(7, dynamic.int), - ) - - #(1, "2", 3, 4, 5, 6, 7, 8) - |> dynamic.from - |> decoder - |> should.equal(Ok(Eight(1, "2", 3, 4, 5, 6, 7, 8))) - - #(1.3, 2.1, 3, 4, 5, 6, 7, 8) - |> dynamic.from - |> decoder - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "String", found: "Float", path: ["1"]), - ]), - ) -} - -type Nine(a, b, c, d, e, f, g, h, i) { - Nine(a, b, c, d, e, f, g, h, i) -} - -pub fn decode9_test() { - let decoder = - dynamic.decode9( - Nine, - dynamic.element(0, dynamic.int), - dynamic.element(1, dynamic.string), - dynamic.element(2, dynamic.int), - dynamic.element(3, dynamic.int), - dynamic.element(4, dynamic.int), - dynamic.element(5, dynamic.int), - dynamic.element(6, dynamic.int), - dynamic.element(7, dynamic.int), - dynamic.element(8, dynamic.int), - ) - - #(1, "2", 3, 4, 5, 6, 7, 8, 9) - |> dynamic.from - |> decoder - |> should.equal(Ok(Nine(1, "2", 3, 4, 5, 6, 7, 8, 9))) - - #(1.3, 2.1, 3, 4, 5, 6, 7, 8, 9) - |> dynamic.from - |> decoder - |> should.equal( - Error([ - DecodeError(expected: "Int", found: "Float", path: ["0"]), - DecodeError(expected: "String", found: "Float", path: ["1"]), - ]), - ) -} - pub fn classify_test() { dynamic.from(True) |> dynamic.classify