import gleam/bit_array as bit_array_mod import gleam/dict.{type Dict} import gleam/int as int_mod import gleam/list as list_mod import gleam/option.{type Option, Some} import gleam/result as result_mod import gleam/string_tree /// `Dynamic` data is data that we don't know the type of yet. /// We likely get data like this from interop with Erlang, or from /// IO with the outside world. /// pub type Dynamic /// Return a string indicating the type of the dynamic value. /// /// This function may be useful for constructing error messages or logs. If you /// want to turn dynamic data into well typed data then you want the /// `gleam/dynamic/decode` module. /// /// ```gleam /// classify(from("Hello")) /// // -> "String" /// ``` /// @external(erlang, "gleam_stdlib", "classify_dynamic") @external(javascript, "../gleam_stdlib.mjs", "classify_dynamic") pub fn classify(data: Dynamic) -> String pub type DecodeError { DecodeError(expected: String, found: String, path: List(String)) } pub type DecodeErrors = List(DecodeError) pub type Decoder(t) = fn(Dynamic) -> Result(t, DecodeErrors) /// Converts any Gleam data into `Dynamic` data. /// @external(erlang, "gleam_stdlib", "identity") @external(javascript, "../gleam_stdlib.mjs", "identity") pub fn from(a: anything) -> Dynamic @deprecated("Please use the gleam/dynamic/decode module") pub fn dynamic(value: Dynamic) -> Result(Dynamic, List(DecodeError)) { Ok(value) } @deprecated("Please use the gleam/dynamic/decode module") pub fn bit_array(from data: Dynamic) -> Result(BitArray, DecodeErrors) { decode_bit_array(data) } @external(erlang, "gleam_stdlib", "decode_bit_array") @external(javascript, "../gleam_stdlib.mjs", "decode_bit_array") fn decode_bit_array(a: Dynamic) -> Result(BitArray, DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") pub fn string(from data: Dynamic) -> Result(String, DecodeErrors) { decode_string(data) } @external(javascript, "../gleam_stdlib.mjs", "decode_string") fn decode_string(from data: Dynamic) -> Result(String, DecodeErrors) { decode_bit_array(data) |> map_errors(put_expected(_, "String")) |> 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: [])]) } }) } fn map_errors( result: Result(a, DecodeErrors), f: fn(DecodeError) -> DecodeError, ) -> Result(a, DecodeErrors) { result_mod.map_error(result, list_mod.map(_, f)) } fn put_expected(error: DecodeError, expected: String) -> DecodeError { DecodeError(..error, expected: expected) } @deprecated("Please use the gleam/dynamic/decode module") pub fn int(from data: Dynamic) -> Result(Int, DecodeErrors) { decode_int(data) } @external(erlang, "gleam_stdlib", "decode_int") @external(javascript, "../gleam_stdlib.mjs", "decode_int") fn decode_int(a: Dynamic) -> Result(Int, DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") pub fn float(from data: Dynamic) -> Result(Float, DecodeErrors) { decode_float(data) } @external(erlang, "gleam_stdlib", "decode_float") @external(javascript, "../gleam_stdlib.mjs", "decode_float") fn decode_float(a: Dynamic) -> Result(Float, DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") pub fn bool(from data: Dynamic) -> Result(Bool, DecodeErrors) { decode_bool(data) } @external(erlang, "gleam_stdlib", "decode_bool") @external(javascript, "../gleam_stdlib.mjs", "decode_bool") fn decode_bool(a: Dynamic) -> Result(Bool, DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") pub fn shallow_list(from value: Dynamic) -> Result(List(Dynamic), DecodeErrors) { decode_list(value) } @external(erlang, "gleam_stdlib", "decode_list") @external(javascript, "../gleam_stdlib.mjs", "decode_list") fn decode_list(a: Dynamic) -> Result(List(Dynamic), DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") pub fn result( ok decode_ok: Decoder(a), error decode_error: Decoder(e), ) -> Decoder(Result(a, e)) { fn(value) { use inner_result <- result_mod.try(decode_result(value)) case inner_result { Ok(raw) -> { use value <- result_mod.try( decode_ok(raw) |> map_errors(push_path(_, "ok")), ) Ok(Ok(value)) } Error(raw) -> { use value <- result_mod.try( decode_error(raw) |> map_errors(push_path(_, "error")), ) Ok(Error(value)) } } } } @external(erlang, "gleam_stdlib", "decode_result") @external(javascript, "../gleam_stdlib.mjs", "decode_result") fn decode_result(a: Dynamic) -> Result(Result(a, e), DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") pub fn list( of decoder_type: fn(Dynamic) -> Result(inner, DecodeErrors), ) -> Decoder(List(inner)) { fn(dynamic) { use list <- result_mod.try(decode_list(dynamic)) list |> list_mod.try_map(decoder_type) |> map_errors(push_path(_, "*")) } } @deprecated("Please use the gleam/dynamic/decode module") pub fn optional(of decode: Decoder(inner)) -> Decoder(Option(inner)) { fn(value) { decode_optional(value, decode) } } @external(erlang, "gleam_stdlib", "decode_option") @external(javascript, "../gleam_stdlib.mjs", "decode_option") fn decode_optional(a: Dynamic, b: Decoder(a)) -> Result(Option(a), DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") 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_mod.try(decode_field(value, name)) maybe_inner |> option.to_result([missing_field_error]) |> result_mod.try(inner_type) |> map_errors(push_path(_, name)) } } @deprecated("Please use the gleam/dynamic/decode module") pub fn optional_field( named name: a, of inner_type: Decoder(t), ) -> Decoder(Option(t)) { fn(value) { 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_mod.map(Some) |> map_errors(push_path(_, name)) } } } @external(erlang, "gleam_stdlib", "decode_field") @external(javascript, "../gleam_stdlib.mjs", "decode_field") fn decode_field(a: Dynamic, b: name) -> Result(Option(Dynamic), DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") pub fn element(at index: Int, of inner_type: Decoder(inner)) -> Decoder(inner) { fn(data: Dynamic) { use tuple <- result_mod.try(decode_tuple(data)) let size = tuple_size(tuple) 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_mod.absolute_value(index) <= size { True -> tuple_get(tuple, size + index) False -> at_least_decode_tuple_error(int_mod.absolute_value(index), data) } }) inner_type(data) |> map_errors(push_path(_, index)) } } fn at_least_decode_tuple_error( size: Int, data: Dynamic, ) -> Result(a, DecodeErrors) { let s = case size { 1 -> "" _ -> "s" } let error = ["Tuple of at least ", int_mod.to_string(size), " element", s] |> string_tree.from_strings |> string_tree.to_string |> DecodeError(found: classify(data), path: []) Error([error]) } // A tuple of unknown size type UnknownTuple @external(erlang, "gleam_stdlib", "decode_tuple") @external(javascript, "../gleam_stdlib.mjs", "decode_tuple") fn decode_tuple(a: Dynamic) -> Result(UnknownTuple, DecodeErrors) @external(erlang, "gleam_stdlib", "decode_tuple2") @external(javascript, "../gleam_stdlib.mjs", "decode_tuple2") fn decode_tuple2(a: Dynamic) -> Result(#(Dynamic, Dynamic), DecodeErrors) @external(erlang, "gleam_stdlib", "decode_tuple3") @external(javascript, "../gleam_stdlib.mjs", "decode_tuple3") fn decode_tuple3( a: Dynamic, ) -> Result(#(Dynamic, Dynamic, Dynamic), DecodeErrors) @external(erlang, "gleam_stdlib", "decode_tuple4") @external(javascript, "../gleam_stdlib.mjs", "decode_tuple4") fn decode_tuple4( a: Dynamic, ) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors) @external(erlang, "gleam_stdlib", "decode_tuple5") @external(javascript, "../gleam_stdlib.mjs", "decode_tuple5") fn decode_tuple5( a: Dynamic, ) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors) @external(erlang, "gleam_stdlib", "decode_tuple6") @external(javascript, "../gleam_stdlib.mjs", "decode_tuple6") fn decode_tuple6( a: Dynamic, ) -> Result( #(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors, ) @external(erlang, "gleam_stdlib", "tuple_get") @external(javascript, "../gleam_stdlib.mjs", "tuple_get") fn tuple_get(a: UnknownTuple, b: Int) -> Result(Dynamic, DecodeErrors) @external(erlang, "gleam_stdlib", "size_of_tuple") @external(javascript, "../gleam_stdlib.mjs", "length") fn tuple_size(a: UnknownTuple) -> Int fn tuple_errors( result: Result(a, List(DecodeError)), name: String, ) -> List(DecodeError) { case result { Ok(_) -> [] Error(errors) -> list_mod.map(errors, push_path(_, name)) } } fn push_path(error: DecodeError, name: t) -> DecodeError { let name = from(name) let decoder = do_any([ decode_string, fn(x) { result_mod.map(decode_int(x), int_mod.to_string) }, ]) let name = case decoder(name) { Ok(name) -> name Error(_) -> ["<", classify(name), ">"] |> string_tree.from_strings |> string_tree.to_string } DecodeError(..error, path: [name, ..error.path]) } @deprecated("Please use the gleam/dynamic/decode module") pub fn tuple2( first decode1: Decoder(a), second decode2: Decoder(b), ) -> Decoder(#(a, b)) { fn(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_mod.append(tuple_errors(b, "1")) |> Error } } } @deprecated("Please use the gleam/dynamic/decode module") 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_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_mod.append(tuple_errors(b, "1")) |> list_mod.append(tuple_errors(c, "2")) |> Error } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn tuple4( first decode1: Decoder(a), second decode2: Decoder(b), third decode3: Decoder(c), fourth decode4: Decoder(d), ) -> Decoder(#(a, b, c, d)) { fn(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_mod.append(tuple_errors(b, "1")) |> list_mod.append(tuple_errors(c, "2")) |> list_mod.append(tuple_errors(d, "3")) |> Error } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn tuple5( first decode1: Decoder(a), second decode2: Decoder(b), third decode3: Decoder(c), fourth decode4: Decoder(d), fifth decode5: Decoder(e), ) -> Decoder(#(a, b, c, d, e)) { fn(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_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 } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn tuple6( first decode1: Decoder(a), second decode2: Decoder(b), third decode3: Decoder(c), fourth decode4: Decoder(d), fifth decode5: Decoder(e), sixth decode6: Decoder(f), ) -> Decoder(#(a, b, c, d, e, f)) { fn(value) { use #(a, b, c, d, e, f) <- result_mod.try(decode_tuple6(value)) case decode1(a), decode2(b), decode3(c), decode4(d), decode5(e), decode6(f) { 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_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 } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn dict( of key_type: Decoder(k), to value_type: Decoder(v), ) -> Decoder(Dict(k, v)) { fn(value) { use dict <- result_mod.try(decode_dict(value)) use pairs <- result_mod.try( dict |> dict.to_list |> list_mod.try_map(fn(pair) { let #(k, v) = pair use k <- result_mod.try( key_type(k) |> map_errors(push_path(_, "keys")), ) use v <- result_mod.try( value_type(v) |> map_errors(push_path(_, "values")), ) Ok(#(k, v)) }), ) Ok(dict.from_list(pairs)) } } @external(erlang, "gleam_stdlib", "decode_map") @external(javascript, "../gleam_stdlib.mjs", "decode_map") fn decode_dict(a: Dynamic) -> Result(Dict(Dynamic, Dynamic), DecodeErrors) @deprecated("Please use the gleam/dynamic/decode module") pub fn any(of decoders: List(Decoder(a))) -> Decoder(a) { do_any(decoders) } fn do_any(of decoders: List(Decoder(a))) -> Decoder(a) { fn(data) { case decoders { [] -> Error([ DecodeError(found: classify(data), expected: "another type", path: []), ]) [decoder, ..decoders] -> case decoder(data) { Ok(decoded) -> Ok(decoded) Error(_) -> do_any(decoders)(data) } } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode1(constructor: fn(t1) -> t, t1: Decoder(t1)) -> Decoder(t) { fn(value) { case t1(value) { Ok(a) -> Ok(constructor(a)) a -> Error(all_errors(a)) } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode2( constructor: fn(t1, t2) -> t, t1: Decoder(t1), t2: Decoder(t2), ) -> Decoder(t) { fn(value) { case t1(value), t2(value) { Ok(a), Ok(b) -> Ok(constructor(a, b)) a, b -> Error(list_mod.flatten([all_errors(a), all_errors(b)])) } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode3( constructor: fn(t1, t2, t3) -> t, t1: Decoder(t1), t2: Decoder(t2), t3: Decoder(t3), ) -> Decoder(t) { fn(value) { case t1(value), t2(value), t3(value) { Ok(a), Ok(b), Ok(c) -> Ok(constructor(a, b, c)) a, b, c -> Error(list_mod.flatten([all_errors(a), all_errors(b), all_errors(c)])) } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode4( constructor: fn(t1, t2, t3, t4) -> t, t1: Decoder(t1), t2: Decoder(t2), t3: Decoder(t3), t4: Decoder(t4), ) -> Decoder(t) { fn(x: Dynamic) { case t1(x), t2(x), t3(x), t4(x) { Ok(a), Ok(b), Ok(c), Ok(d) -> Ok(constructor(a, b, c, d)) a, b, c, d -> Error( list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), all_errors(d), ]), ) } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode5( constructor: fn(t1, t2, t3, t4, t5) -> t, t1: Decoder(t1), t2: Decoder(t2), t3: Decoder(t3), t4: Decoder(t4), t5: Decoder(t5), ) -> Decoder(t) { fn(x: Dynamic) { case t1(x), t2(x), t3(x), t4(x), t5(x) { Ok(a), Ok(b), Ok(c), Ok(d), Ok(e) -> Ok(constructor(a, b, c, d, e)) a, b, c, d, e -> Error( list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), all_errors(d), all_errors(e), ]), ) } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode6( constructor: fn(t1, t2, t3, t4, t5, t6) -> t, t1: Decoder(t1), t2: Decoder(t2), t3: Decoder(t3), t4: Decoder(t4), t5: Decoder(t5), t6: Decoder(t6), ) -> Decoder(t) { fn(x: Dynamic) { case t1(x), t2(x), t3(x), t4(x), t5(x), t6(x) { Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f) -> Ok(constructor(a, b, c, d, e, f)) a, b, c, d, e, f -> Error( list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), all_errors(d), all_errors(e), all_errors(f), ]), ) } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode7( constructor: fn(t1, t2, t3, t4, t5, t6, t7) -> t, t1: Decoder(t1), t2: Decoder(t2), t3: Decoder(t3), t4: Decoder(t4), t5: Decoder(t5), t6: Decoder(t6), t7: Decoder(t7), ) -> Decoder(t) { fn(x: Dynamic) { case t1(x), t2(x), t3(x), t4(x), t5(x), t6(x), t7(x) { Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f), Ok(g) -> Ok(constructor(a, b, c, d, e, f, g)) a, b, c, d, e, f, g -> Error( list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), all_errors(d), all_errors(e), all_errors(f), all_errors(g), ]), ) } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode8( constructor: fn(t1, t2, t3, t4, t5, t6, t7, t8) -> t, t1: Decoder(t1), t2: Decoder(t2), t3: Decoder(t3), t4: Decoder(t4), t5: Decoder(t5), t6: Decoder(t6), t7: Decoder(t7), t8: Decoder(t8), ) -> Decoder(t) { fn(x: Dynamic) { case t1(x), t2(x), t3(x), t4(x), t5(x), t6(x), t7(x), t8(x) { Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f), Ok(g), Ok(h) -> Ok(constructor(a, b, c, d, e, f, g, h)) a, b, c, d, e, f, g, h -> Error( list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), all_errors(d), all_errors(e), all_errors(f), all_errors(g), all_errors(h), ]), ) } } } @deprecated("Please use the gleam/dynamic/decode module") pub fn decode9( constructor: fn(t1, t2, t3, t4, t5, t6, t7, t8, t9) -> t, t1: Decoder(t1), t2: Decoder(t2), t3: Decoder(t3), t4: Decoder(t4), t5: Decoder(t5), t6: Decoder(t6), t7: Decoder(t7), t8: Decoder(t8), t9: Decoder(t9), ) -> Decoder(t) { fn(x: Dynamic) { case t1(x), t2(x), t3(x), t4(x), t5(x), t6(x), t7(x), t8(x), t9(x) { Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f), Ok(g), Ok(h), Ok(i) -> Ok(constructor(a, b, c, d, e, f, g, h, i)) a, b, c, d, e, f, g, h, i -> Error( list_mod.flatten([ all_errors(a), all_errors(b), all_errors(c), all_errors(d), all_errors(e), all_errors(f), all_errors(g), all_errors(h), all_errors(i), ]), ) } } } fn all_errors(result: Result(a, List(DecodeError))) -> List(DecodeError) { case result { Ok(_) -> [] Error(errors) -> errors } }