From cbdd74a5e78a85be8d4d49a93b87e501e5b0aef5 Mon Sep 17 00:00:00 2001 From: sharno Date: Mon, 26 Oct 2020 22:01:48 -0400 Subject: Closes #115: Add dynamic.option --- CHANGELOG.md | 1 + gleam | Bin 0 -> 19306232 bytes src/gleam/dynamic.gleam | 35 +++++++++++++++++++++++++++++++++-- test/gleam/dynamic_test.gleam | 18 ++++++++++++++++++ 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 gleam diff --git a/CHANGELOG.md b/CHANGELOG.md index 1956083..b45b807 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - The `list` module gains the `each`, and `partition` functions. - The `int` and `float` modules gain the `negate` function. - The `result` module gains the `all` function. +- The `dynamic` module gains the `option` function. ## v0.11.0 - 2020-08-22 diff --git a/gleam b/gleam new file mode 100644 index 0000000..7f13854 Binary files /dev/null and b/gleam differ diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam index bf7963f..e4656c4 100644 --- a/src/gleam/dynamic.gleam +++ b/src/gleam/dynamic.gleam @@ -1,8 +1,9 @@ import gleam/bit_string.{BitString} as bit_string_mod import gleam/list as list_mod -import gleam/atom +import gleam/atom as atom_mod import gleam/map.{Map} import gleam/result +import gleam/option.{None, Option, Some} /// `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 @@ -102,7 +103,7 @@ pub external fn float(from: Dynamic) -> Result(Float, String) = /// > atom(from(123)) /// Error("Expected an Atom, got `123`") /// -pub external fn atom(from: Dynamic) -> Result(atom.Atom, String) = +pub external fn atom(from: Dynamic) -> Result(atom_mod.Atom, String) = "gleam_stdlib" "decode_atom" /// Check to see whether a Dynamic value is an bool, and return the bool if @@ -182,6 +183,36 @@ pub fn typed_list( |> result.then(list_mod.try_map(_, decoder_type)) } +/// Check to see if a Dynamic value is an Option of a particular type, and return +/// the Option if it is. +/// +/// The second argument is a decoder function used to decode the elements of +/// the list. The list is only decoded if all elements in the list can be +/// successfully decoded using this function. +/// +/// ## Examples +/// +/// > option(from("Hello"), string) +/// Ok(Some("Hello")) +/// +/// > option(from(atom.from_string("null")), string) +/// Ok(None) +/// +/// > option(from(123), string) +/// Error("Expected a bit_string, got an int") +/// +pub fn option( + from dynamic: Dynamic, + of decoder: Decoder(inner), +) -> Result(Option(inner), String) { + let Ok(null) = atom_mod.from_string("null") + case atom(dynamic), decoder(dynamic) { + Ok(atom), _ if atom == null -> Ok(None) + _, Ok(result) -> Ok(Some(result)) + _, Error(msg) -> Error(msg) + } +} + /// Check to see if a Dynamic value is a map with a specific field, and return /// the value of the field if it is. /// diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam index 2b272d4..f4e78a0 100644 --- a/test/gleam/dynamic_test.gleam +++ b/test/gleam/dynamic_test.gleam @@ -5,6 +5,7 @@ import gleam/list import gleam/should import gleam/result import gleam/map +import gleam/option.{None, Some} pub fn bit_string_test() { "" @@ -220,6 +221,23 @@ pub fn typed_list_test() { |> should.be_error } +pub fn option_test() { + let Ok(null) = atom.from_string("null") + + 1 + |> dynamic.from + |> dynamic.option(dynamic.int) + |> should.equal(Ok(Some(1))) + null + |> dynamic.from + |> dynamic.option(dynamic.int) + |> should.equal(Ok(None)) + 1 + |> dynamic.from + |> dynamic.option(dynamic.string) + |> should.be_error +} + pub fn field_test() { let Ok(ok_atom) = atom.from_string("ok") let Ok(error_atom) = atom.from_string("error") -- cgit v1.2.3