Skip to content

Commit 928f8fb

Browse files
committed
v0.51.0
1 parent cbec8f5 commit 928f8fb

File tree

6 files changed

+79
-39
lines changed

6 files changed

+79
-39
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## v0.51.0 - 2024-12-22
4+
5+
- `dynamic/decode` now has its own error type.
6+
- The `new_primitive_decoder` function in the `dynamic/decode` has a new API.
7+
38
## v0.50.0 - 2024-12-22
49

510
- The `dynamic/decode` module has been added. This module will replace the

gleam.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name = "gleam_stdlib"
2-
version = "0.50.0"
2+
version = "0.51.0"
33
gleam = ">= 0.32.0"
44
licences = ["Apache-2.0"]
55
description = "A standard library for the Gleam programming language"

src/gleam/dynamic/decode.gleam

+41-28
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@
257257
//// ```
258258

259259
import gleam/dict.{type Dict}
260-
import gleam/dynamic.{type DecodeError, DecodeError}
260+
import gleam/dynamic
261261
import gleam/int
262262
import gleam/list
263263
import gleam/option.{type Option, None, Some}
@@ -271,14 +271,20 @@ import gleam/result
271271
pub type Dynamic =
272272
dynamic.Dynamic
273273

274+
/// Error returned when unexpected data is encountered
275+
///
276+
pub type DecodeError {
277+
DecodeError(expected: String, found: String, path: List(String))
278+
}
279+
274280
/// A decoder is a value that can be used to turn dynamically typed `Dynamic`
275281
/// data into typed data using the `run` function.
276282
///
277283
/// Several smaller decoders can be combined to make larger decoders using
278284
/// functions such as `list` and `field`.
279285
///
280286
pub opaque type Decoder(t) {
281-
Decoder(function: fn(Dynamic) -> #(t, List(dynamic.DecodeError)))
287+
Decoder(function: fn(Dynamic) -> #(t, List(DecodeError)))
282288
}
283289

284290
/// The same as [`field`](#field), except taking a path to the value rather
@@ -339,10 +345,7 @@ pub fn subfield(
339345
/// decode.run(data, decoder)
340346
/// ```
341347
///
342-
pub fn run(
343-
data: Dynamic,
344-
decoder: Decoder(t),
345-
) -> Result(t, List(dynamic.DecodeError)) {
348+
pub fn run(data: Dynamic, decoder: Decoder(t)) -> Result(t, List(DecodeError)) {
346349
let #(maybe_invalid_data, errors) = decoder.function(data)
347350
case errors {
348351
[] -> Ok(maybe_invalid_data)
@@ -392,10 +395,10 @@ pub fn at(path: List(segment), inner: Decoder(a)) -> Decoder(a) {
392395
fn index(
393396
path: List(a),
394397
position: List(a),
395-
inner: fn(Dynamic) -> #(b, List(dynamic.DecodeError)),
398+
inner: fn(Dynamic) -> #(b, List(DecodeError)),
396399
data: Dynamic,
397-
handle_miss: fn(Dynamic, List(a)) -> #(b, List(dynamic.DecodeError)),
398-
) -> #(b, List(dynamic.DecodeError)) {
400+
handle_miss: fn(Dynamic, List(a)) -> #(b, List(DecodeError)),
401+
) -> #(b, List(DecodeError)) {
399402
case path {
400403
[] -> {
401404
inner(data)
@@ -477,7 +480,7 @@ pub fn success(data: t) -> Decoder(t) {
477480
pub fn decode_error(
478481
expected expected: String,
479482
found found: Dynamic,
480-
) -> List(dynamic.DecodeError) {
483+
) -> List(DecodeError) {
481484
[DecodeError(expected: expected, found: dynamic.classify(found), path: [])]
482485
}
483486

@@ -601,10 +604,14 @@ fn run_dynamic_function(
601604
data: Dynamic,
602605
zero: t,
603606
f: dynamic.Decoder(t),
604-
) -> #(t, List(dynamic.DecodeError)) {
607+
) -> #(t, List(DecodeError)) {
605608
case f(data) {
606609
Ok(data) -> #(data, [])
607-
Error(errors) -> #(zero, errors)
610+
Error(errors) -> {
611+
let errors =
612+
list.map(errors, fn(e) { DecodeError(e.expected, e.found, e.path) })
613+
#(zero, errors)
614+
}
608615
}
609616
}
610617

@@ -619,7 +626,7 @@ fn run_dynamic_function(
619626
///
620627
pub const string: Decoder(String) = Decoder(decode_string)
621628

622-
fn decode_string(data: Dynamic) -> #(String, List(dynamic.DecodeError)) {
629+
fn decode_string(data: Dynamic) -> #(String, List(DecodeError)) {
623630
run_dynamic_function(data, "", dynamic.string)
624631
}
625632

@@ -634,7 +641,7 @@ fn decode_string(data: Dynamic) -> #(String, List(dynamic.DecodeError)) {
634641
///
635642
pub const bool: Decoder(Bool) = Decoder(decode_bool)
636643

637-
fn decode_bool(data: Dynamic) -> #(Bool, List(dynamic.DecodeError)) {
644+
fn decode_bool(data: Dynamic) -> #(Bool, List(DecodeError)) {
638645
run_dynamic_function(data, False, dynamic.bool)
639646
}
640647

@@ -649,7 +656,7 @@ fn decode_bool(data: Dynamic) -> #(Bool, List(dynamic.DecodeError)) {
649656
///
650657
pub const int: Decoder(Int) = Decoder(decode_int)
651658

652-
fn decode_int(data: Dynamic) -> #(Int, List(dynamic.DecodeError)) {
659+
fn decode_int(data: Dynamic) -> #(Int, List(DecodeError)) {
653660
run_dynamic_function(data, 0, dynamic.int)
654661
}
655662

@@ -664,7 +671,7 @@ fn decode_int(data: Dynamic) -> #(Int, List(dynamic.DecodeError)) {
664671
///
665672
pub const float: Decoder(Float) = Decoder(decode_float)
666673

667-
fn decode_float(data: Dynamic) -> #(Float, List(dynamic.DecodeError)) {
674+
fn decode_float(data: Dynamic) -> #(Float, List(DecodeError)) {
668675
run_dynamic_function(data, 0.0, dynamic.float)
669676
}
670677

@@ -679,7 +686,7 @@ fn decode_float(data: Dynamic) -> #(Float, List(dynamic.DecodeError)) {
679686
///
680687
pub const dynamic: Decoder(Dynamic) = Decoder(decode_dynamic)
681688

682-
fn decode_dynamic(data: Dynamic) -> #(Dynamic, List(dynamic.DecodeError)) {
689+
fn decode_dynamic(data: Dynamic) -> #(Dynamic, List(DecodeError)) {
683690
#(data, [])
684691
}
685692

@@ -694,7 +701,7 @@ fn decode_dynamic(data: Dynamic) -> #(Dynamic, List(dynamic.DecodeError)) {
694701
///
695702
pub const bit_array: Decoder(BitArray) = Decoder(decode_bit_array)
696703

697-
fn decode_bit_array(data: Dynamic) -> #(BitArray, List(dynamic.DecodeError)) {
704+
fn decode_bit_array(data: Dynamic) -> #(BitArray, List(DecodeError)) {
698705
run_dynamic_function(data, <<>>, dynamic.bit_array)
699706
}
700707

@@ -719,11 +726,11 @@ pub fn list(of inner: Decoder(a)) -> Decoder(List(a)) {
719726
@external(javascript, "../../gleam_stdlib_decode_ffi.mjs", "list")
720727
fn decode_list(
721728
data: Dynamic,
722-
item: fn(Dynamic) -> #(t, List(dynamic.DecodeError)),
729+
item: fn(Dynamic) -> #(t, List(DecodeError)),
723730
push_path: fn(#(t, List(DecodeError)), key) -> #(t, List(DecodeError)),
724731
index: Int,
725732
acc: List(t),
726-
) -> #(List(t), List(dynamic.DecodeError))
733+
) -> #(List(t), List(DecodeError))
727734

728735
/// A decoder that decodes dicts where all keys and vales are decoded with
729736
/// given decoders.
@@ -762,12 +769,12 @@ pub fn dict(
762769
}
763770

764771
fn fold_dict(
765-
acc: #(Dict(k, v), List(dynamic.DecodeError)),
772+
acc: #(Dict(k, v), List(DecodeError)),
766773
key: Dynamic,
767774
value: Dynamic,
768-
key_decoder: fn(Dynamic) -> #(k, List(dynamic.DecodeError)),
769-
value_decoder: fn(Dynamic) -> #(v, List(dynamic.DecodeError)),
770-
) -> #(Dict(k, v), List(dynamic.DecodeError)) {
775+
key_decoder: fn(Dynamic) -> #(k, List(DecodeError)),
776+
value_decoder: fn(Dynamic) -> #(v, List(DecodeError)),
777+
) -> #(Dict(k, v), List(DecodeError)) {
771778
// First we decode the key.
772779
case key_decoder(key) {
773780
#(key, []) ->
@@ -966,18 +973,24 @@ pub fn failure(zero: a, expected: String) -> Decoder(a) {
966973
/// import decode/decode
967974
///
968975
/// pub fn string_decoder() -> decode.Decoder(String) {
969-
/// decode.new_primitive_decoder(dynamic.string, "")
976+
/// let default = ""
977+
/// decode.new_primitive_decoder("String", fn(data) {
978+
/// case dynamic.string {
979+
/// Ok(x) -> Ok(x)
980+
/// Error(x) -> Error(default)
981+
/// }
982+
/// })
970983
/// }
971984
/// ```
972985
///
973986
pub fn new_primitive_decoder(
974-
decoding_function: fn(Dynamic) -> Result(t, List(DecodeError)),
975-
zero: t,
987+
name: String,
988+
decoding_function: fn(Dynamic) -> Result(t, t),
976989
) -> Decoder(t) {
977990
Decoder(function: fn(d) {
978991
case decoding_function(d) {
979992
Ok(t) -> #(t, [])
980-
Error(errors) -> #(zero, errors)
993+
Error(zero) -> #(zero, [DecodeError(name, dynamic.classify(d), [])])
981994
}
982995
})
983996
}

src/gleam_stdlib_decode_ffi.mjs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Ok, Error, List, NonEmpty } from "./gleam.mjs";
22
import { default as Dict } from "./dict.mjs";
33
import { Some, None } from "./gleam/option.mjs";
4-
import { DecodeError, classify } from "./gleam/dynamic.mjs";
4+
import { classify } from "./gleam/dynamic.mjs";
5+
import { DecodeError } from "./gleam/dynamic/decode.mjs";
56

67
export function strict_index(data, key) {
78
const int = Number.isInteger(key);

test/gleam/dynamic/decode_test.gleam

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import gleam/dict
2-
import gleam/dynamic.{type Dynamic, DecodeError}
3-
import gleam/dynamic/decode
2+
import gleam/dynamic.{type Dynamic}
3+
import gleam/dynamic/decode.{DecodeError}
44
import gleam/float
55
import gleam/int
66
import gleam/option
7+
import gleam/result
78
import gleam/should
89

910
pub type User {
@@ -768,29 +769,45 @@ pub fn documentation_variants_example_test() {
768769
}
769770

770771
pub fn new_primitive_decoder_string_ok_test() {
772+
let decoder =
773+
decode.new_primitive_decoder("String", fn(x) {
774+
dynamic.string(x) |> result.replace_error("")
775+
})
771776
dynamic.from("Hello!")
772-
|> decode.run(decode.new_primitive_decoder(dynamic.string, ""))
777+
|> decode.run(decoder)
773778
|> should.be_ok
774779
|> should.equal("Hello!")
775780
}
776781

777782
pub fn new_primitive_decoder_string_error_test() {
783+
let decoder =
784+
decode.new_primitive_decoder("String", fn(x) {
785+
dynamic.string(x) |> result.replace_error("")
786+
})
778787
dynamic.from(123)
779-
|> decode.run(decode.new_primitive_decoder(dynamic.string, ""))
788+
|> decode.run(decoder)
780789
|> should.be_error
781790
|> should.equal([DecodeError("String", "Int", [])])
782791
}
783792

784793
pub fn new_primitive_decoder_float_ok_test() {
794+
let decoder =
795+
decode.new_primitive_decoder("Float", fn(x) {
796+
dynamic.float(x) |> result.replace_error(0.0)
797+
})
785798
dynamic.from(12.4)
786-
|> decode.run(decode.new_primitive_decoder(dynamic.float, 0.0))
799+
|> decode.run(decoder)
787800
|> should.be_ok
788801
|> should.equal(12.4)
789802
}
790803

791804
pub fn new_primitive_decoder_float_error_test() {
805+
let decoder =
806+
decode.new_primitive_decoder("Float", fn(x) {
807+
dynamic.float(x) |> result.replace_error(0.0)
808+
})
792809
dynamic.from("blah")
793-
|> decode.run(decode.new_primitive_decoder(dynamic.float, 0.0))
810+
|> decode.run(decoder)
794811
|> should.be_error
795812
|> should.equal([DecodeError("Float", "String", [])])
796813
}

test/gleam/string_test.gleam

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import gleam/dict
2-
import gleam/int
3-
import gleam/list
42
import gleam/option.{None, Some}
53
import gleam/order
6-
import gleam/result
74
import gleam/should
85
import gleam/string
96

7+
@target(erlang)
8+
import gleam/int
9+
@target(erlang)
10+
import gleam/list
11+
@target(erlang)
12+
import gleam/result
13+
1014
pub fn length_test() {
1115
string.length("ß↑e̊")
1216
|> should.equal(3)

0 commit comments

Comments
 (0)