aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gleam/dynamic.gleam76
-rw-r--r--test/gleam/dynamic_test.gleam26
2 files changed, 73 insertions, 29 deletions
diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam
index 30a297c..e75f1ad 100644
--- a/src/gleam/dynamic.gleam
+++ b/src/gleam/dynamic.gleam
@@ -833,34 +833,52 @@ if javascript {
"../gleam_stdlib.mjs" "decode_map"
}
-if erlang {
- /// Joins multiple decoders into one. When run they will each be tried in turn
- /// until one succeeds, or they all fail.
- ///
- /// ## Examples
- ///
- /// > import gleam/result
- /// > let bool_or_string = any(_, of: [
- /// > string,
- /// > fn(x) { result.map(bool(x), fn(_) { "a bool" }) }
- /// > ])
- /// > bool_or_string(from("ok"))
- /// Ok("ok")
- ///
- /// > bool_or_string(from(True))
- /// Ok("a bool")
- ///
- /// > bool_or_string(from(1))
- /// Error(DecodeError(expected: "unknown", found: "unknown"))
- ///
- pub fn any(
- from data: Dynamic,
- of decoders: List(Decoder(t)),
- ) -> Result(t, DecodeError) {
- decoders
- |> list.find_map(fn(decoder) { decoder(data) })
- |> result.map_error(fn(_) {
- DecodeError(expected: "unknown", found: "unknown")
- })
+/// Joins multiple decoders into one. When run they will each be tried in turn
+/// until one succeeds, or they all fail.
+///
+/// ## Examples
+///
+/// > import gleam/result
+/// > let bool_or_string = any(_, of: [
+/// > string,
+/// > fn(x) { result.map(bool(x), fn(_) { "a bool" }) }
+/// > ])
+/// > bool_or_string(from("ok"))
+/// Ok("ok")
+///
+/// > bool_or_string(from(True))
+/// Ok("a bool")
+///
+/// > bool_or_string(from(1))
+/// Error(DecodeError(expected: "unknown", found: "unknown"))
+///
+pub fn any(
+ from data: Dynamic,
+ of decoders: List(Decoder(t)),
+) -> Result(t, DecodeError) {
+ do_any(data, decoders, [])
+}
+
+fn do_any(
+ data: Dynamic,
+ decoders: List(Decoder(a)),
+ failed: List(String),
+) -> Result(a, DecodeError) {
+ case decoders {
+ [] -> {
+ let expected =
+ failed
+ |> list.intersperse(" or ")
+ |> string_builder.from_strings
+ |> string_builder.to_string
+ Error(DecodeError(found: classify(data), expected: expected))
+ }
+
+ [decoder, ..decoders] ->
+ case decoder(data) {
+ Ok(decoded) -> Ok(decoded)
+ Error(DecodeError(expected: expected, ..)) ->
+ do_any(data, decoders, [expected, ..failed])
+ }
}
}
diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam
index 57dfdae..8529b9a 100644
--- a/test/gleam/dynamic_test.gleam
+++ b/test/gleam/dynamic_test.gleam
@@ -1,6 +1,7 @@
import gleam/should
import gleam/dynamic.{DecodeError}
import gleam/bit_string
+import gleam/result
import gleam/map
import gleam/option.{None, Some}
@@ -793,3 +794,28 @@ pub fn result_test() {
|> dynamic.result(ok: dynamic.int, error: dynamic.string)
|> should.equal(Error(DecodeError(expected: "Result", found: "Int")))
}
+
+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("Float or Int", "String")))
+}