diff options
author | Louis Pilfold <louis@lpil.uk> | 2024-12-19 12:48:12 +0000 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2024-12-22 10:56:21 +0000 |
commit | f43a6366e44062a5928340599591048a89f7398f (patch) | |
tree | 60f81bfb2919b8a6d67da23f02dae09ae7eb3a0a | |
parent | 6dc7a93fba3f1fee0913efbe2889dc1e564f3ffc (diff) | |
download | gleam_stdlib-f43a6366e44062a5928340599591048a89f7398f.tar.gz gleam_stdlib-f43a6366e44062a5928340599591048a89f7398f.zip |
Recursive decoder
-rw-r--r-- | src/gleam/dynamic/decode.gleam | 31 | ||||
-rw-r--r-- | test/gleam/dynamic/decode_test.gleam | 26 |
2 files changed, 57 insertions, 0 deletions
diff --git a/src/gleam/dynamic/decode.gleam b/src/gleam/dynamic/decode.gleam index 42755d5..bd68c50 100644 --- a/src/gleam/dynamic/decode.gleam +++ b/src/gleam/dynamic/decode.gleam @@ -981,3 +981,34 @@ pub fn new_primitive_decoder( } }) } + +/// Create a decoder that can refer to itself, useful for decoding for 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 function +/// then you may not need to use this function. +/// +/// ```gleam +/// import gleam/dynamic +/// import decode/zero.{type Decoder} +/// +/// type Nested { +/// Nested(List(Nested)) +/// Value(String) +/// } +/// +/// fn nested_decoder() -> Decoder(Nested) { +/// use <- zero.recursive +/// zero.one_of(zero.string |> zero.map(Value), [ +/// zero.list(nested_decoder()) |> zero.map(Nested), +/// ]) +/// } +/// ``` +/// +pub fn recursive(inner: fn() -> Decoder(a)) -> Decoder(a) { + Decoder(function: fn(data) { + let decoder = inner() + decoder.function(data) + }) +} diff --git a/test/gleam/dynamic/decode_test.gleam b/test/gleam/dynamic/decode_test.gleam index 582f2af..b176c70 100644 --- a/test/gleam/dynamic/decode_test.gleam +++ b/test/gleam/dynamic/decode_test.gleam @@ -927,3 +927,29 @@ pub fn js_map_test() { |> should.be_ok |> should.equal(dict.from_list([#("a", 10), #("b", 20), #("c", 30)])) } + +type Nested { + Nested(List(Nested)) + Value(String) +} + +fn recursive_decoder() -> decode.Decoder(Nested) { + use <- decode.recursive() + decode.one_of(decode.string |> decode.map(Value), [ + decode.list(recursive_decoder()) |> decode.map(Nested), + ]) +} + +pub fn recursive_test() { + let nested = [["one", "two"], ["three"], []] + let expected = + Nested([ + Nested([Value("one"), Value("two")]), + Nested([Value("three")]), + Nested([]), + ]) + + decode.run(dynamic.from(nested), recursive_decoder()) + |> should.be_ok + |> should.equal(expected) +} |