Initial commit
This commit is contained in:
commit
a6272848f9
379 changed files with 74829 additions and 0 deletions
947
build/dev/javascript/gleam_stdlib/gleam/dynamic/decode.mjs
Normal file
947
build/dev/javascript/gleam_stdlib/gleam/dynamic/decode.mjs
Normal file
|
|
@ -0,0 +1,947 @@
|
|||
import {
|
||||
Ok,
|
||||
Error,
|
||||
toList,
|
||||
Empty as $Empty,
|
||||
prepend as listPrepend,
|
||||
CustomType as $CustomType,
|
||||
isEqual,
|
||||
} from "../../gleam.mjs";
|
||||
import * as $bit_array from "../../gleam/bit_array.mjs";
|
||||
import * as $dict from "../../gleam/dict.mjs";
|
||||
import * as $dynamic from "../../gleam/dynamic.mjs";
|
||||
import * as $int from "../../gleam/int.mjs";
|
||||
import * as $list from "../../gleam/list.mjs";
|
||||
import * as $option from "../../gleam/option.mjs";
|
||||
import { None, Some } from "../../gleam/option.mjs";
|
||||
import {
|
||||
index as bare_index,
|
||||
int as dynamic_int,
|
||||
float as dynamic_float,
|
||||
bit_array as dynamic_bit_array,
|
||||
list as decode_list,
|
||||
dict as decode_dict,
|
||||
identity as cast,
|
||||
is_null,
|
||||
string as dynamic_string,
|
||||
} from "../../gleam_stdlib.mjs";
|
||||
|
||||
export class DecodeError extends $CustomType {
|
||||
constructor(expected, found, path) {
|
||||
super();
|
||||
this.expected = expected;
|
||||
this.found = found;
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
export const DecodeError$DecodeError = (expected, found, path) =>
|
||||
new DecodeError(expected, found, path);
|
||||
export const DecodeError$isDecodeError = (value) =>
|
||||
value instanceof DecodeError;
|
||||
export const DecodeError$DecodeError$expected = (value) => value.expected;
|
||||
export const DecodeError$DecodeError$0 = (value) => value.expected;
|
||||
export const DecodeError$DecodeError$found = (value) => value.found;
|
||||
export const DecodeError$DecodeError$1 = (value) => value.found;
|
||||
export const DecodeError$DecodeError$path = (value) => value.path;
|
||||
export const DecodeError$DecodeError$2 = (value) => value.path;
|
||||
|
||||
class Decoder extends $CustomType {
|
||||
constructor(function$) {
|
||||
super();
|
||||
this.function = function$;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a decoder on a `Dynamic` value, decoding the value if it is of the
|
||||
* desired type, or returning errors.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let decoder = {
|
||||
* use name <- decode.field("email", decode.string)
|
||||
* use email <- decode.field("password", decode.string)
|
||||
* decode.success(SignUp(name: name, email: email))
|
||||
* }
|
||||
*
|
||||
* decode.run(data, decoder)
|
||||
* ```
|
||||
*/
|
||||
export function run(data, decoder) {
|
||||
let $ = decoder.function(data);
|
||||
let maybe_invalid_data;
|
||||
let errors;
|
||||
maybe_invalid_data = $[0];
|
||||
errors = $[1];
|
||||
if (errors instanceof $Empty) {
|
||||
return new Ok(maybe_invalid_data);
|
||||
} else {
|
||||
return new Error(errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalise a decoder having successfully extracted a value.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let data = dynamic.properties([
|
||||
* #(dynamic.string("email"), dynamic.string("lucy@example.com")),
|
||||
* #(dynamic.string("name"), dynamic.string("Lucy")),
|
||||
* ]))
|
||||
*
|
||||
* let decoder = {
|
||||
* use name <- decode.field("name", string)
|
||||
* use email <- decode.field("email", string)
|
||||
* decode.success(SignUp(name: name, email: email))
|
||||
* }
|
||||
*
|
||||
* let result = decode.run(data, decoder)
|
||||
* assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
|
||||
* ```
|
||||
*/
|
||||
export function success(data) {
|
||||
return new Decoder((_) => { return [data, toList([])]; });
|
||||
}
|
||||
|
||||
function decode_dynamic(data) {
|
||||
return [data, toList([])];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a transformation function to any value decoded by the decoder.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let decoder = decode.int |> decode.map(int.to_string)
|
||||
* let result = decode.run(dynamic.int(1000), decoder)
|
||||
* assert result == Ok("1000")
|
||||
* ```
|
||||
*/
|
||||
export function map(decoder, transformer) {
|
||||
return new Decoder(
|
||||
(d) => {
|
||||
let $ = decoder.function(d);
|
||||
let data;
|
||||
let errors;
|
||||
data = $[0];
|
||||
errors = $[1];
|
||||
return [transformer(data), errors];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a transformation function to any errors returned by the decoder.
|
||||
*/
|
||||
export function map_errors(decoder, transformer) {
|
||||
return new Decoder(
|
||||
(d) => {
|
||||
let $ = decoder.function(d);
|
||||
let data;
|
||||
let errors;
|
||||
data = $[0];
|
||||
errors = $[1];
|
||||
return [data, transformer(errors)];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new decoder based upon the value of a previous decoder.
|
||||
*
|
||||
* This may be useful to run one previous decoder to use in further decoding.
|
||||
*/
|
||||
export function then$(decoder, next) {
|
||||
return new Decoder(
|
||||
(dynamic_data) => {
|
||||
let $ = decoder.function(dynamic_data);
|
||||
let data;
|
||||
let errors;
|
||||
data = $[0];
|
||||
errors = $[1];
|
||||
let decoder$1 = next(data);
|
||||
let $1 = decoder$1.function(dynamic_data);
|
||||
let layer;
|
||||
let data$1;
|
||||
layer = $1;
|
||||
data$1 = $1[0];
|
||||
if (errors instanceof $Empty) {
|
||||
return layer;
|
||||
} else {
|
||||
return [data$1, errors];
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function run_decoders(loop$data, loop$failure, loop$decoders) {
|
||||
while (true) {
|
||||
let data = loop$data;
|
||||
let failure = loop$failure;
|
||||
let decoders = loop$decoders;
|
||||
if (decoders instanceof $Empty) {
|
||||
return failure;
|
||||
} else {
|
||||
let decoder = decoders.head;
|
||||
let decoders$1 = decoders.tail;
|
||||
let $ = decoder.function(data);
|
||||
let layer;
|
||||
let errors;
|
||||
layer = $;
|
||||
errors = $[1];
|
||||
if (errors instanceof $Empty) {
|
||||
return layer;
|
||||
} else {
|
||||
loop$data = data;
|
||||
loop$failure = failure;
|
||||
loop$decoders = decoders$1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new decoder from several other decoders. Each of the inner
|
||||
* decoders is run in turn, and the value from the first to succeed is used.
|
||||
*
|
||||
* If no decoder succeeds then the errors from the first decoder is used.
|
||||
* If you wish for different errors then you may wish to use the
|
||||
* `collapse_errors` or `map_errors` functions.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let decoder = decode.one_of(decode.string, or: [
|
||||
* decode.int |> decode.map(int.to_string),
|
||||
* decode.float |> decode.map(float.to_string),
|
||||
* ])
|
||||
* decode.run(dynamic.int(1000), decoder)
|
||||
* // -> Ok("1000")
|
||||
* ```
|
||||
*/
|
||||
export function one_of(first, alternatives) {
|
||||
return new Decoder(
|
||||
(dynamic_data) => {
|
||||
let $ = first.function(dynamic_data);
|
||||
let layer;
|
||||
let errors;
|
||||
layer = $;
|
||||
errors = $[1];
|
||||
if (errors instanceof $Empty) {
|
||||
return layer;
|
||||
} else {
|
||||
return run_decoders(dynamic_data, layer, alternatives);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a decoder that can refer to itself, useful for decoding deeply
|
||||
* nested data.
|
||||
*
|
||||
* Attempting to create a recursive decoder without this function could result
|
||||
* in an infinite loop. If you are using `field` or other `use`able functions
|
||||
* then you may not need to use this function.
|
||||
*
|
||||
* ```gleam
|
||||
* type Nested {
|
||||
* Nested(List(Nested))
|
||||
* Value(String)
|
||||
* }
|
||||
*
|
||||
* fn nested_decoder() -> decode.Decoder(Nested) {
|
||||
* use <- decode.recursive
|
||||
* decode.one_of(decode.string |> decode.map(Value), [
|
||||
* decode.list(nested_decoder()) |> decode.map(Nested),
|
||||
* ])
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function recursive(inner) {
|
||||
return new Decoder(
|
||||
(data) => {
|
||||
let decoder = inner();
|
||||
return decoder.function(data);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoder that decodes nullable values of a type decoded by with a given
|
||||
* decoder.
|
||||
*
|
||||
* This function can handle common representations of null on all runtimes, such as
|
||||
* `nil`, `null`, and `undefined` on Erlang, and `undefined` and `null` on
|
||||
* JavaScript.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let result = decode.run(dynamic.int(100), decode.optional(decode.int))
|
||||
* assert result == Ok(option.Some(100))
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* let result = decode.run(dynamic.nil(), decode.optional(decode.int))
|
||||
* assert result == Ok(option.None)
|
||||
* ```
|
||||
*/
|
||||
export function optional(inner) {
|
||||
return new Decoder(
|
||||
(data) => {
|
||||
let $ = is_null(data);
|
||||
if ($) {
|
||||
return [new $option.None(), toList([])];
|
||||
} else {
|
||||
let $1 = inner.function(data);
|
||||
let data$1;
|
||||
let errors;
|
||||
data$1 = $1[0];
|
||||
errors = $1[1];
|
||||
return [new $option.Some(data$1), errors];
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoder that decodes `Dynamic` values. This decoder never returns an error.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let result = decode.run(dynamic.float(3.14), decode.dynamic)
|
||||
* assert result == Ok(dynamic.float(3.14))
|
||||
* ```
|
||||
*/
|
||||
export const dynamic = /* @__PURE__ */ new Decoder(decode_dynamic);
|
||||
|
||||
/**
|
||||
* Construct a decode error for some unexpected dynamic data.
|
||||
*/
|
||||
export function decode_error(expected, found) {
|
||||
return toList([
|
||||
new DecodeError(expected, $dynamic.classify(found), toList([])),
|
||||
]);
|
||||
}
|
||||
|
||||
function run_dynamic_function(data, name, f) {
|
||||
let $ = f(data);
|
||||
if ($ instanceof Ok) {
|
||||
let data$1 = $[0];
|
||||
return [data$1, toList([])];
|
||||
} else {
|
||||
let zero = $[0];
|
||||
return [
|
||||
zero,
|
||||
toList([new DecodeError(name, $dynamic.classify(data), toList([]))]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function decode_bool(data) {
|
||||
let $ = isEqual(cast(true), data);
|
||||
if ($) {
|
||||
return [true, toList([])];
|
||||
} else {
|
||||
let $1 = isEqual(cast(false), data);
|
||||
if ($1) {
|
||||
return [false, toList([])];
|
||||
} else {
|
||||
return [false, decode_error("Bool", data)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function decode_int(data) {
|
||||
return run_dynamic_function(data, "Int", dynamic_int);
|
||||
}
|
||||
|
||||
function decode_float(data) {
|
||||
return run_dynamic_function(data, "Float", dynamic_float);
|
||||
}
|
||||
|
||||
function decode_bit_array(data) {
|
||||
return run_dynamic_function(data, "BitArray", dynamic_bit_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all errors produced by a decoder with one single error for a named
|
||||
* expected type.
|
||||
*
|
||||
* This function may be useful if you wish to simplify errors before
|
||||
* presenting them to a user, particularly when using the `one_of` function.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let decoder = decode.string |> decode.collapse_errors("MyThing")
|
||||
* let result = decode.run(dynamic.int(1000), decoder)
|
||||
* assert result == Error([DecodeError("MyThing", "Int", [])])
|
||||
* ```
|
||||
*/
|
||||
export function collapse_errors(decoder, name) {
|
||||
return new Decoder(
|
||||
(dynamic_data) => {
|
||||
let $ = decoder.function(dynamic_data);
|
||||
let layer;
|
||||
let data;
|
||||
let errors;
|
||||
layer = $;
|
||||
data = $[0];
|
||||
errors = $[1];
|
||||
if (errors instanceof $Empty) {
|
||||
return layer;
|
||||
} else {
|
||||
return [data, decode_error(name, dynamic_data)];
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a decoder that always fails. The parameter for this function is the
|
||||
* name of the type that has failed to decode.
|
||||
*/
|
||||
export function failure(zero, expected) {
|
||||
return new Decoder((d) => { return [zero, decode_error(expected, d)]; });
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a decoder for a new data type from a decoding function.
|
||||
*
|
||||
* This function is used for new primitive types. For example, you might
|
||||
* define a decoder for Erlang's pid type.
|
||||
*
|
||||
* A default "zero" value is also required to make a decoder. When this
|
||||
* decoder is used as part of a larger decoder this zero value used as
|
||||
* a placeholder so that the rest of the decoder can continue to run and
|
||||
* collect all decoding errors.
|
||||
*
|
||||
* If you were to make a decoder for the `String` type (rather than using the
|
||||
* build-in `string` decoder) you would define it like so:
|
||||
*
|
||||
* ```gleam
|
||||
* pub fn string_decoder() -> decode.Decoder(String) {
|
||||
* let default = ""
|
||||
* decode.new_primitive_decoder("String", fn(data) {
|
||||
* case dynamic.string(data) {
|
||||
* Ok(x) -> Ok(x)
|
||||
* Error(_) -> Error(default)
|
||||
* }
|
||||
* })
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function new_primitive_decoder(name, decoding_function) {
|
||||
return new Decoder(
|
||||
(d) => {
|
||||
let $ = decoding_function(d);
|
||||
if ($ instanceof Ok) {
|
||||
let t = $[0];
|
||||
return [t, toList([])];
|
||||
} else {
|
||||
let zero = $[0];
|
||||
return [
|
||||
zero,
|
||||
toList([new DecodeError(name, $dynamic.classify(d), toList([]))]),
|
||||
];
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoder that decodes `Bool` values.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let result = decode.run(dynamic.bool(True), decode.bool)
|
||||
* assert result == Ok(True)
|
||||
* ```
|
||||
*/
|
||||
export const bool = /* @__PURE__ */ new Decoder(decode_bool);
|
||||
|
||||
/**
|
||||
* A decoder that decodes `Int` values.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let result = decode.run(dynamic.int(147), decode.int)
|
||||
* assert result == Ok(147)
|
||||
* ```
|
||||
*/
|
||||
export const int = /* @__PURE__ */ new Decoder(decode_int);
|
||||
|
||||
/**
|
||||
* A decoder that decodes `Float` values.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let result = decode.run(dynamic.float(3.14), decode.float)
|
||||
* assert result == Ok(3.14)
|
||||
* ```
|
||||
*/
|
||||
export const float = /* @__PURE__ */ new Decoder(decode_float);
|
||||
|
||||
/**
|
||||
* A decoder that decodes `BitArray` values. This decoder never returns an error.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let result = decode.run(dynamic.bit_array(<<5, 7>>), decode.bit_array)
|
||||
* assert result == Ok(<<5, 7>>)
|
||||
* ```
|
||||
*/
|
||||
export const bit_array = /* @__PURE__ */ new Decoder(decode_bit_array);
|
||||
|
||||
function decode_string(data) {
|
||||
return run_dynamic_function(data, "String", dynamic_string);
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoder that decodes `String` values.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let result = decode.run(dynamic.string("Hello!"), decode.string)
|
||||
* assert result == Ok("Hello!")
|
||||
* ```
|
||||
*/
|
||||
export const string = /* @__PURE__ */ new Decoder(decode_string);
|
||||
|
||||
function fold_dict(acc, key, value, key_decoder, value_decoder) {
|
||||
let $ = key_decoder(key);
|
||||
let $1 = $[1];
|
||||
if ($1 instanceof $Empty) {
|
||||
let key$1 = $[0];
|
||||
let $2 = value_decoder(value);
|
||||
let $3 = $2[1];
|
||||
if ($3 instanceof $Empty) {
|
||||
let value$1 = $2[0];
|
||||
let dict$1 = $dict.insert(acc[0], key$1, value$1);
|
||||
return [dict$1, acc[1]];
|
||||
} else {
|
||||
let errors = $3;
|
||||
return push_path([$dict.new$(), errors], toList(["values"]));
|
||||
}
|
||||
} else {
|
||||
let errors = $1;
|
||||
return push_path([$dict.new$(), errors], toList(["keys"]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoder that decodes dicts where all keys and vales are decoded with
|
||||
* given decoders.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let values = dynamic.properties([
|
||||
* #(dynamic.string("one"), dynamic.int(1)),
|
||||
* #(dynamic.string("two"), dynamic.int(2)),
|
||||
* ])
|
||||
*
|
||||
* let result =
|
||||
* decode.run(values, decode.dict(decode.string, decode.int))
|
||||
* assert result == Ok(values)
|
||||
* ```
|
||||
*/
|
||||
export function dict(key, value) {
|
||||
return new Decoder(
|
||||
(data) => {
|
||||
let $ = decode_dict(data);
|
||||
if ($ instanceof Ok) {
|
||||
let dict$1 = $[0];
|
||||
return $dict.fold(
|
||||
dict$1,
|
||||
[$dict.new$(), toList([])],
|
||||
(a, k, v) => {
|
||||
let $1 = a[1];
|
||||
if ($1 instanceof $Empty) {
|
||||
return fold_dict(a, k, v, key.function, value.function);
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return [$dict.new$(), decode_error("Dict", data)];
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoder that decodes lists where all elements are decoded with a given
|
||||
* decoder.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let result =
|
||||
* [1, 2, 3]
|
||||
* |> list.map(dynamic.int)
|
||||
* |> dynamic.list
|
||||
* |> decode.run(decode.list(of: decode.int))
|
||||
* assert result == Ok([1, 2, 3])
|
||||
* ```
|
||||
*/
|
||||
export function list(inner) {
|
||||
return new Decoder(
|
||||
(data) => {
|
||||
return decode_list(
|
||||
data,
|
||||
inner.function,
|
||||
(p, k) => { return push_path(p, toList([k])); },
|
||||
0,
|
||||
toList([]),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function push_path(layer, path) {
|
||||
let decoder = one_of(
|
||||
string,
|
||||
toList([
|
||||
(() => {
|
||||
let _pipe = int;
|
||||
return map(_pipe, $int.to_string);
|
||||
})(),
|
||||
]),
|
||||
);
|
||||
let path$1 = $list.map(
|
||||
path,
|
||||
(key) => {
|
||||
let key$1 = cast(key);
|
||||
let $ = run(key$1, decoder);
|
||||
if ($ instanceof Ok) {
|
||||
let key$2 = $[0];
|
||||
return key$2;
|
||||
} else {
|
||||
return ("<" + $dynamic.classify(key$1)) + ">";
|
||||
}
|
||||
},
|
||||
);
|
||||
let errors = $list.map(
|
||||
layer[1],
|
||||
(error) => {
|
||||
return new DecodeError(
|
||||
error.expected,
|
||||
error.found,
|
||||
$list.append(path$1, error.path),
|
||||
);
|
||||
},
|
||||
);
|
||||
return [layer[0], errors];
|
||||
}
|
||||
|
||||
function index(
|
||||
loop$path,
|
||||
loop$position,
|
||||
loop$inner,
|
||||
loop$data,
|
||||
loop$handle_miss
|
||||
) {
|
||||
while (true) {
|
||||
let path = loop$path;
|
||||
let position = loop$position;
|
||||
let inner = loop$inner;
|
||||
let data = loop$data;
|
||||
let handle_miss = loop$handle_miss;
|
||||
if (path instanceof $Empty) {
|
||||
let _pipe = data;
|
||||
let _pipe$1 = inner(_pipe);
|
||||
return push_path(_pipe$1, $list.reverse(position));
|
||||
} else {
|
||||
let key = path.head;
|
||||
let path$1 = path.tail;
|
||||
let $ = bare_index(data, key);
|
||||
if ($ instanceof Ok) {
|
||||
let $1 = $[0];
|
||||
if ($1 instanceof Some) {
|
||||
let data$1 = $1[0];
|
||||
loop$path = path$1;
|
||||
loop$position = listPrepend(key, position);
|
||||
loop$inner = inner;
|
||||
loop$data = data$1;
|
||||
loop$handle_miss = handle_miss;
|
||||
} else {
|
||||
return handle_miss(data, listPrepend(key, position));
|
||||
}
|
||||
} else {
|
||||
let kind = $[0];
|
||||
let $1 = inner(data);
|
||||
let default$;
|
||||
default$ = $1[0];
|
||||
let _pipe = [
|
||||
default$,
|
||||
toList([new DecodeError(kind, $dynamic.classify(data), toList([]))]),
|
||||
];
|
||||
return push_path(_pipe, $list.reverse(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as [`field`](#field), except taking a path to the value rather
|
||||
* than a field name.
|
||||
*
|
||||
* This function will index into dictionaries with any key type, and if the key is
|
||||
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
|
||||
* the first eight elements of Gleam lists.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let data = dynamic.properties([
|
||||
* #(dynamic.string("data"), dynamic.properties([
|
||||
* #(dynamic.string("email"), dynamic.string("lucy@example.com")),
|
||||
* #(dynamic.string("name"), dynamic.string("Lucy")),
|
||||
* ])
|
||||
* ]))
|
||||
*
|
||||
* let decoder = {
|
||||
* use name <- decode.subfield(["data", "name"], decode.string)
|
||||
* use email <- decode.subfield(["data", "email"], decode.string)
|
||||
* decode.success(SignUp(name: name, email: email))
|
||||
* }
|
||||
* let result = decode.run(data, decoder)
|
||||
* assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
|
||||
* ```
|
||||
*/
|
||||
export function subfield(field_path, field_decoder, next) {
|
||||
return new Decoder(
|
||||
(data) => {
|
||||
let $ = index(
|
||||
field_path,
|
||||
toList([]),
|
||||
field_decoder.function,
|
||||
data,
|
||||
(data, position) => {
|
||||
let $1 = field_decoder.function(data);
|
||||
let default$;
|
||||
default$ = $1[0];
|
||||
let _pipe = [
|
||||
default$,
|
||||
toList([new DecodeError("Field", "Nothing", toList([]))]),
|
||||
];
|
||||
return push_path(_pipe, $list.reverse(position));
|
||||
},
|
||||
);
|
||||
let out;
|
||||
let errors1;
|
||||
out = $[0];
|
||||
errors1 = $[1];
|
||||
let $1 = next(out).function(data);
|
||||
let out$1;
|
||||
let errors2;
|
||||
out$1 = $1[0];
|
||||
errors2 = $1[1];
|
||||
return [out$1, $list.append(errors1, errors2)];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoder that decodes a value that is nested within other values. For
|
||||
* example, decoding a value that is within some deeply nested JSON objects.
|
||||
*
|
||||
* This function will index into dictionaries with any key type, and if the key is
|
||||
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
|
||||
* the first eight elements of Gleam lists.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let decoder = decode.at(["one", "two"], decode.int)
|
||||
*
|
||||
* let data = dynamic.properties([
|
||||
* #(dynamic.string("one"), dynamic.properties([
|
||||
* #(dynamic.string("two"), dynamic.int(1000)),
|
||||
* ])),
|
||||
* ]))
|
||||
*
|
||||
*
|
||||
* decode.run(data, decoder)
|
||||
* // -> Ok(1000)
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* dynamic.nil()
|
||||
* |> decode.run(decode.optional(decode.int))
|
||||
* // -> Ok(option.None)
|
||||
* ```
|
||||
*/
|
||||
export function at(path, inner) {
|
||||
return new Decoder(
|
||||
(data) => {
|
||||
return index(
|
||||
path,
|
||||
toList([]),
|
||||
inner.function,
|
||||
data,
|
||||
(data, position) => {
|
||||
let $ = inner.function(data);
|
||||
let default$;
|
||||
default$ = $[0];
|
||||
let _pipe = [
|
||||
default$,
|
||||
toList([new DecodeError("Field", "Nothing", toList([]))]),
|
||||
];
|
||||
return push_path(_pipe, $list.reverse(position));
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a decoder on a field of a `Dynamic` value, decoding the value if it is
|
||||
* of the desired type, or returning errors. An error is returned if there is
|
||||
* no field for the specified key.
|
||||
*
|
||||
* This function will index into dictionaries with any key type, and if the key is
|
||||
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
|
||||
* the first eight elements of Gleam lists.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let data = dynamic.properties([
|
||||
* #(dynamic.string("email"), dynamic.string("lucy@example.com")),
|
||||
* #(dynamic.string("name"), dynamic.string("Lucy")),
|
||||
* ]))
|
||||
*
|
||||
* let decoder = {
|
||||
* use name <- decode.field("name", string)
|
||||
* use email <- decode.field("email", string)
|
||||
* decode.success(SignUp(name: name, email: email))
|
||||
* }
|
||||
*
|
||||
* let result = decode.run(data, decoder)
|
||||
* assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
|
||||
* ```
|
||||
*
|
||||
* If you wish to decode a value that is more deeply nested within the dynamic
|
||||
* data, see [`subfield`](#subfield) and [`at`](#at).
|
||||
*
|
||||
* If you wish to return a default in the event that a field is not present,
|
||||
* see [`optional_field`](#optional_field) and / [`optionally_at`](#optionally_at).
|
||||
*/
|
||||
export function field(field_name, field_decoder, next) {
|
||||
return subfield(toList([field_name]), field_decoder, next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a decoder on a field of a `Dynamic` value, decoding the value if it is
|
||||
* of the desired type, or returning errors. The given default value is
|
||||
* returned if there is no field for the specified key.
|
||||
*
|
||||
* This function will index into dictionaries with any key type, and if the key is
|
||||
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
|
||||
* the first eight elements of Gleam lists.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let data = dynamic.properties([
|
||||
* #(dynamic.string("name"), dynamic.string("Lucy")),
|
||||
* ]))
|
||||
*
|
||||
* let decoder = {
|
||||
* use name <- decode.field("name", string)
|
||||
* use email <- decode.optional_field("email", "n/a", string)
|
||||
* decode.success(SignUp(name: name, email: email))
|
||||
* }
|
||||
*
|
||||
* let result = decode.run(data, decoder)
|
||||
* assert result == Ok(SignUp(name: "Lucy", email: "n/a"))
|
||||
* ```
|
||||
*/
|
||||
export function optional_field(key, default$, field_decoder, next) {
|
||||
return new Decoder(
|
||||
(data) => {
|
||||
let _block;
|
||||
let _block$1;
|
||||
let $1 = bare_index(data, key);
|
||||
if ($1 instanceof Ok) {
|
||||
let $2 = $1[0];
|
||||
if ($2 instanceof Some) {
|
||||
let data$1 = $2[0];
|
||||
_block$1 = field_decoder.function(data$1);
|
||||
} else {
|
||||
_block$1 = [default$, toList([])];
|
||||
}
|
||||
} else {
|
||||
let kind = $1[0];
|
||||
_block$1 = [
|
||||
default$,
|
||||
toList([new DecodeError(kind, $dynamic.classify(data), toList([]))]),
|
||||
];
|
||||
}
|
||||
let _pipe = _block$1;
|
||||
_block = push_path(_pipe, toList([key]));
|
||||
let $ = _block;
|
||||
let out;
|
||||
let errors1;
|
||||
out = $[0];
|
||||
errors1 = $[1];
|
||||
let $2 = next(out).function(data);
|
||||
let out$1;
|
||||
let errors2;
|
||||
out$1 = $2[0];
|
||||
errors2 = $2[1];
|
||||
return [out$1, $list.append(errors1, errors2)];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoder that decodes a value that is nested within other values. For
|
||||
* example, decoding a value that is within some deeply nested JSON objects.
|
||||
*
|
||||
* This function will index into dictionaries with any key type, and if the key is
|
||||
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
|
||||
* the first eight elements of Gleam lists.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* let decoder = decode.optionally_at(["one", "two"], 100, decode.int)
|
||||
*
|
||||
* let data = dynamic.properties([
|
||||
* #(dynamic.string("one"), dynamic.properties([])),
|
||||
* ]))
|
||||
*
|
||||
*
|
||||
* decode.run(data, decoder)
|
||||
* // -> Ok(100)
|
||||
* ```
|
||||
*/
|
||||
export function optionally_at(path, default$, inner) {
|
||||
return new Decoder(
|
||||
(data) => {
|
||||
return index(
|
||||
path,
|
||||
toList([]),
|
||||
inner.function,
|
||||
data,
|
||||
(_, _1) => { return [default$, toList([])]; },
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue