aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-09-09 19:44:35 +0100
committerLouis Pilfold <louis@lpil.uk>2021-09-09 19:44:35 +0100
commitc45c1038f8b12acf7d2010eba070ad3dc54765a2 (patch)
tree39ceed83733a3c54ba4048122a5b4d0b8ce823ed
parentc5d415fdc206ae3403b2992c9a3e395188b45280 (diff)
downloadgleam_stdlib-c45c1038f8b12acf7d2010eba070ad3dc54765a2.tar.gz
gleam_stdlib-c45c1038f8b12acf7d2010eba070ad3dc54765a2.zip
JS dynamic string
-rw-r--r--src/gleam/dynamic.gleam114
-rw-r--r--src/gleam_stdlib.js23
-rw-r--r--test/gleam/dynamic_test.gleam51
3 files changed, 129 insertions, 59 deletions
diff --git a/src/gleam/dynamic.gleam b/src/gleam/dynamic.gleam
index 98cbef0..ccc621e 100644
--- a/src/gleam/dynamic.gleam
+++ b/src/gleam/dynamic.gleam
@@ -1,39 +1,66 @@
+import gleam/bit_string
+import gleam/list
+import gleam/map
+import gleam/option
+import gleam/result
+import gleam/string_builder
+
+if erlang {
+ import gleam/map.{Map}
+ import gleam/option.{Option}
+}
+
/// `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
/// IO with the outside world.
pub external type Dynamic
-if erlang {
- import gleam/bit_string
- import gleam/list
- import gleam/map.{Map}
- import gleam/option.{Option}
- import gleam/result
- import gleam/string_builder
+/// Error returned when unexpected data is encountered
+pub type DecodeError {
+ DecodeError(expected: String, found: String)
+}
- /// Error returned when unexpected data is encountered
- pub type DecodeError {
- DecodeError(expected: String, found: String)
- }
+pub type Decoder(t) =
+ fn(Dynamic) -> Result(t, DecodeError)
- pub type Decoder(t) =
- fn(Dynamic) -> Result(t, DecodeError)
+/// Converts any Gleam data into `Dynamic` data.
+///
+pub fn from(a) -> Dynamic {
+ do_from(a)
+}
- /// Converts any Gleam data into `Dynamic` data.
- ///
- pub external fn from(a) -> Dynamic =
+if erlang {
+ external fn do_from(anything) -> Dynamic =
"gleam_stdlib" "identity"
+}
- /// Unsafely casts a Dynamic value into any other type.
- ///
- /// This is an escape hatch for the type system that may be useful when wrapping
- /// native Erlang APIs. It is to be used as a last measure only!
- ///
- /// If you can avoid using this function, do!
- ///
- pub external fn unsafe_coerce(Dynamic) -> a =
+if javascript {
+ external fn do_from(anything) -> Dynamic =
+ "../gleam_stdlib.js" "identity"
+}
+
+/// Unsafely casts a Dynamic value into any other type.
+///
+/// This is an escape hatch for the type system that may be useful when wrapping
+/// native Erlang APIs. It is to be used as a last measure only!
+///
+/// If you can avoid using this function, do!
+///
+pub fn unsafe_coerce(a: Dynamic) -> anything {
+ do_unsafe_coerce(a)
+}
+
+if erlang {
+ external fn do_unsafe_coerce(Dynamic) -> a =
"gleam_stdlib" "identity"
+}
+if javascript {
+ external fn do_unsafe_coerce(Dynamic) -> a =
+ "../gleam_stdlib.js" "identity"
+}
+
+if erlang {
/// Checks to see whether a Dynamic value is a bit_string, and return the bit_string if
/// it is.
///
@@ -47,20 +74,26 @@ if erlang {
///
pub external fn bit_string(from: Dynamic) -> Result(BitString, DecodeError) =
"gleam_stdlib" "decode_bit_string"
+}
- /// Checks to see whether a Dynamic value is a string, and return the string if
- /// it is.
- ///
- /// ## Examples
- ///
- /// > string(from("Hello"))
- /// Ok("Hello")
- ///
- /// > string(from(123))
- /// Error(DecodeError(expected: "String", found: "Int"))
- ///
- pub fn string(from: Dynamic) -> Result(String, DecodeError) {
- bit_string(from)
+/// Checks to see whether a Dynamic value is a string, and return the string if
+/// it is.
+///
+/// ## Examples
+///
+/// > string(from("Hello"))
+/// Ok("Hello")
+///
+/// > string(from(123))
+/// Error(DecodeError(expected: "String", found: "Int"))
+///
+pub fn string(from data: Dynamic) -> Result(String, DecodeError) {
+ decode_string(data)
+}
+
+if erlang {
+ fn decode_string(data: Dynamic) -> Result(String, DecodeError) {
+ bit_string(data)
|> result.map_error(fn(error) { DecodeError(..error, expected: "String") })
|> result.then(fn(raw) {
case bit_string.to_string(raw) {
@@ -69,7 +102,14 @@ if erlang {
}
})
}
+}
+if javascript {
+ external fn decode_string(Dynamic) -> Result(String, DecodeError) =
+ "../gleam_stdlib.js" "decode_string"
+}
+
+if erlang {
/// Checks to see whether a Dynamic value is an int, and return the int if it
/// is.
///
diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js
index bb63cbb..75b0882 100644
--- a/src/gleam_stdlib.js
+++ b/src/gleam_stdlib.js
@@ -12,6 +12,7 @@ import {
CompileError as RegexCompileError,
Match as RegexMatch,
} from "./gleam/regex.js";
+import { DecodeError } from "./gleam/dynamic.js";
import { Some, None } from "./gleam/option.js";
const HASHCODE_CACHE = new WeakMap();
@@ -467,3 +468,25 @@ export function decode64(sBase64) {
return new Ok(new BitString(taBytes));
}
+
+function classify_dynamic(data) {
+ if (typeof data === "string") {
+ return "String";
+ } else if (List.isList(data)) {
+ return "List";
+ } else if (Number.isInteger(data)) {
+ return "Int";
+ } else {
+ return typeof data;
+ }
+}
+
+function decoder_error(expected, got) {
+ return new Error(new DecodeError(expected, classify_dynamic(got)));
+}
+
+export function decode_string(data) {
+ return typeof data === "string"
+ ? new Ok(data)
+ : decoder_error("String", data);
+}
diff --git a/test/gleam/dynamic_test.gleam b/test/gleam/dynamic_test.gleam
index 5f6ae21..df92ffb 100644
--- a/test/gleam/dynamic_test.gleam
+++ b/test/gleam/dynamic_test.gleam
@@ -1,8 +1,9 @@
+import gleam/should
+import gleam/dynamic.{DecodeError}
+
if erlang {
import gleam/bit_string
- import gleam/dynamic.{DecodeError}
import gleam/list
- import gleam/should
import gleam/result
import gleam/map
import gleam/option.{None, Some}
@@ -33,34 +34,40 @@ if erlang {
|> dynamic.bit_string
|> should.equal(Error(DecodeError(expected: "BitString", found: "List")))
}
+}
- pub fn string_test() {
- ""
- |> dynamic.from
- |> dynamic.string
- |> should.equal(Ok(""))
-
- "Hello"
- |> dynamic.from
- |> dynamic.string
- |> should.equal(Ok("Hello"))
+pub fn string_test() {
+ ""
+ |> dynamic.from
+ |> dynamic.string
+ |> should.equal(Ok(""))
+
+ "Hello"
+ |> dynamic.from
+ |> dynamic.string
+ |> should.equal(Ok("Hello"))
+
+ 1
+ |> dynamic.from
+ |> dynamic.string
+ |> should.equal(Error(DecodeError(expected: "String", found: "Int")))
+
+ []
+ |> dynamic.from
+ |> dynamic.string
+ |> should.equal(Error(DecodeError(expected: "String", found: "List")))
+}
+if erlang {
+ pub fn string_non_utf8_test() {
<<65535:16>>
|> dynamic.from
|> dynamic.string
|> should.equal(Error(DecodeError(expected: "String", found: "BitString")))
-
- 1
- |> dynamic.from
- |> dynamic.string
- |> should.equal(Error(DecodeError(expected: "String", found: "Int")))
-
- []
- |> dynamic.from
- |> dynamic.string
- |> should.equal(Error(DecodeError(expected: "String", found: "List")))
}
+}
+if erlang {
pub fn int_test() {
1
|> dynamic.from