aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-09-04 23:06:09 +0100
committerLouis Pilfold <louis@lpil.uk>2021-09-04 23:06:09 +0100
commit7a52f1356fe39475b7576ca5f105a10ae975e7ff (patch)
tree26aaef910ccba182d38474630bb904af732934f2
parenta54b1986daf44ebfd941849584175215306ad6d9 (diff)
downloadgleam_stdlib-7a52f1356fe39475b7576ca5f105a10ae975e7ff.tar.gz
gleam_stdlib-7a52f1356fe39475b7576ca5f105a10ae975e7ff.zip
JS parse query
-rw-r--r--src/gleam/uri.gleam55
-rw-r--r--src/gleam_stdlib.erl15
-rw-r--r--src/gleam_stdlib.js18
-rw-r--r--test/gleam/uri_test.gleam290
4 files changed, 201 insertions, 177 deletions
diff --git a/src/gleam/uri.gleam b/src/gleam/uri.gleam
index e67837e..5482478 100644
--- a/src/gleam/uri.gleam
+++ b/src/gleam/uri.gleam
@@ -189,39 +189,34 @@ fn extra_required(list: List(a), remaining: Int) -> Int {
}
}
-if erlang {
- import gleam/io
-
- external fn erl_parse_query(String) -> Dynamic =
- "uri_string" "dissect_query"
+/// Parses an urlencoded query string into a list of key value pairs.
+/// Returns an error for invalid encoding.
+///
+/// The opposite operation is `uri.query_to_string`.
+///
+/// ## Examples
+///
+/// ```
+/// > parse_query("a=1&b=2")
+///
+/// Ok([#("a", "1"), #("b", "2")])
+/// ```
+///
+pub fn parse_query(query: String) -> Result(List(#(String, String)), Nil) {
+ do_parse_query(query)
+}
- /// Parses an urlencoded query string into a list of key value pairs.
- /// Returns an error for invalid encoding.
- ///
- /// The opposite operation is `uri.query_to_string`.
- ///
- /// ## Examples
- ///
- /// ```
- /// > parse_query("a=1&b=2")
- ///
- /// Ok([#("a", "1"), #("b", "2")])
- /// ```
- ///
- pub fn parse_query(query: String) -> Result(List(#(String, String)), Nil) {
- let bool_value = fn(x) { result.map(dynamic.bool(x), fn(_) { "" }) }
- let query_param = dynamic.typed_tuple2(
- _,
- dynamic.string,
- dynamic.any(_, of: [dynamic.string, bool_value]),
- )
+if erlang {
+ external fn do_parse_query(String) -> Result(List(#(String, String)), Nil) =
+ "gleam_stdlib" "parse_query"
+}
- query
- |> erl_parse_query
- |> dynamic.typed_list(of: query_param)
- |> result.nil_error
- }
+if javascript {
+ external fn do_parse_query(String) -> Result(List(#(String, String)), Nil) =
+ "../gleam_stdlib.js" "parse_query"
+}
+if erlang {
type Encoding {
Utf8
}
diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl
index 408853a..f8f6851 100644
--- a/src/gleam_stdlib.erl
+++ b/src/gleam_stdlib.erl
@@ -5,13 +5,13 @@
map_get/2, iodata_append/2, identity/1, decode_int/1, decode_bool/1,
decode_float/1, decode_thunk/1, decode_list/1, decode_optional/2,
decode_field/2, decode_element/2, parse_int/1, parse_float/1,
- less_than/2, string_pop_grapheme/1, string_starts_with/2,
+ less_than/2, string_pop_grapheme/1, string_starts_with/2, wrap_list/1,
string_ends_with/2, string_pad/4, decode_tuple2/1, decode_tuple3/1,
decode_tuple4/1, decode_tuple5/1, decode_tuple6/1, decode_map/1,
bit_string_int_to_u32/1, bit_string_int_from_u32/1, decode_result/1,
bit_string_slice/3, decode_bit_string/1, compile_regex/2,
regex_check/2, regex_split/2, regex_scan/2, base_decode64/1,
- wrap_list/1, bit_string_concat/1]).
+ parse_query/1, bit_string_concat/1]).
should_equal(Actual, Expected) ->
?assertEqual(Expected, Actual),
@@ -234,3 +234,14 @@ base_decode64(S) ->
wrap_list(X) when is_list(X) -> X;
wrap_list(X) -> [X].
+
+parse_query(Query) ->
+ case uri_string:dissect_query(Query) of
+ {error, _, _} -> {error, nil};
+ Pairs ->
+ Pairs1 = lists:map(fun
+ ({K, true}) -> {K, <<"">>};
+ (Pair) -> Pair
+ end, Pairs),
+ {ok, Pairs1}
+ end.
diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js
index 360de75..082e661 100644
--- a/src/gleam_stdlib.js
+++ b/src/gleam_stdlib.js
@@ -352,3 +352,21 @@ export function map_get(map, key) {
export function map_insert(key, value, map) {
return map.insert(key, value);
}
+
+function decode_query_component(string) {
+ return decodeURIComponent((string || "").replace("+", " "));
+}
+
+export function parse_query(query) {
+ try {
+ let pairs = [];
+ for (let section of query.split("&")) {
+ let [key, value] = section.split("=");
+ if (!key) continue;
+ pairs.push([decode_query_component(key), decode_query_component(value)]);
+ }
+ return new Ok(List.fromArray(pairs));
+ } catch (error) {
+ return new Error(Nil);
+ }
+}
diff --git a/test/gleam/uri_test.gleam b/test/gleam/uri_test.gleam
index e184c94..ca00200 100644
--- a/test/gleam/uri_test.gleam
+++ b/test/gleam/uri_test.gleam
@@ -294,32 +294,32 @@ pub fn port_to_string_test() {
|> should.equal("noslash")
}
-if erlang {
- pub fn parse_query_string_test() {
- assert Ok(parsed) = uri.parse_query("weebl+bob=1&city=%C3%B6rebro")
- should.equal(parsed, [#("weebl bob", "1"), #("city", "örebro")])
+pub fn parse_query_string_test() {
+ assert Ok(parsed) = uri.parse_query("weebl+bob=1&city=%C3%B6rebro")
+ should.equal(parsed, [#("weebl bob", "1"), #("city", "örebro")])
- // Duplicates keys not overridden
- assert Ok(parsed) = uri.parse_query("a[]=1&a[]=2")
+ // Duplicates keys not overridden
+ assert Ok(parsed) = uri.parse_query("a[]=1&a[]=2")
- parsed
- |> should.equal([#("a[]", "1"), #("a[]", "2")])
- }
+ parsed
+ |> should.equal([#("a[]", "1"), #("a[]", "2")])
+}
- pub fn parse_empty_query_string_test() {
- assert Ok(parsed) = uri.parse_query("")
- should.equal(parsed, [])
- }
+pub fn parse_empty_query_string_test() {
+ assert Ok(parsed) = uri.parse_query("")
+ should.equal(parsed, [])
+}
- pub fn parse_query_string_with_empty_test() {
- uri.parse_query("present")
- |> should.equal(Ok([#("present", "")]))
- }
+pub fn parse_query_string_with_empty_test() {
+ uri.parse_query("present")
+ |> should.equal(Ok([#("present", "")]))
+}
- pub fn error_parsing_query_test() {
- should.equal(uri.parse_query("%C2"), Error(Nil))
- }
+pub fn error_parsing_query_test() {
+ should.equal(uri.parse_query("%C2"), Error(Nil))
+}
+if erlang {
pub fn query_to_string_test() {
let query_string =
uri.query_to_string([#("weebl bob", "1"), #("city", "örebro")])
@@ -416,151 +416,151 @@ if erlang {
should.equal(uri.path_segments("../bob"), ["bob"])
should.equal(uri.path_segments("/weebl/../bob"), ["bob"])
}
+}
- pub fn origin1_test() {
- let parsed = uri.parse("http://example.test/path?weebl#bob")
- uri.origin(parsed)
- |> should.equal(Ok("http://example.test/"))
- }
+pub fn origin1_test() {
+ let parsed = uri.parse("http://example.test/path?weebl#bob")
+ uri.origin(parsed)
+ |> should.equal(Ok("http://example.test/"))
+}
- pub fn origin2_test() {
- let parsed = uri.parse("http://example.test:8080")
- uri.origin(parsed)
- |> should.equal(Ok("http://example.test:8080/"))
- }
+pub fn origin2_test() {
+ let parsed = uri.parse("http://example.test:8080")
+ uri.origin(parsed)
+ |> should.equal(Ok("http://example.test:8080/"))
+}
- pub fn origin3_test() {
- let parsed = uri.parse("https://example.test")
- uri.origin(parsed)
- |> should.equal(Ok("https://example.test/"))
- }
+pub fn origin3_test() {
+ let parsed = uri.parse("https://example.test")
+ uri.origin(parsed)
+ |> should.equal(Ok("https://example.test/"))
+}
- pub fn origin4_test() {
- let parsed = uri.parse("http:///path")
- uri.origin(parsed)
- |> should.equal(Ok("http://"))
- }
+pub fn origin4_test() {
+ let parsed = uri.parse("http:///path")
+ uri.origin(parsed)
+ |> should.equal(Ok("http://"))
+}
- pub fn origin5_test() {
- let parsed = uri.parse("http://")
- uri.origin(parsed)
- |> should.equal(Ok("http://"))
- }
+pub fn origin5_test() {
+ let parsed = uri.parse("http://")
+ uri.origin(parsed)
+ |> should.equal(Ok("http://"))
+}
- pub fn origin6_test() {
- let parsed = uri.parse("/path")
- uri.origin(parsed)
- |> should.equal(Error(Nil))
- }
+pub fn origin6_test() {
+ let parsed = uri.parse("/path")
+ uri.origin(parsed)
+ |> should.equal(Error(Nil))
+}
- pub fn origin7_test() {
- let parsed = uri.parse("file:///dev/null")
- uri.origin(parsed)
- |> should.equal(Error(Nil))
- }
+pub fn origin7_test() {
+ let parsed = uri.parse("file:///dev/null")
+ uri.origin(parsed)
+ |> should.equal(Error(Nil))
+}
- pub fn merge1_test() {
- let a = uri.parse("/relative")
- let b = uri.parse("")
- uri.merge(a, b)
- |> should.equal(Error(Nil))
- }
+pub fn merge1_test() {
+ let a = uri.parse("/relative")
+ let b = uri.parse("")
+ uri.merge(a, b)
+ |> should.equal(Error(Nil))
+}
- pub fn merge2_test() {
- let a = uri.parse("http://google.com/weebl")
- let b = uri.parse("http://example.com/baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/baz")))
- }
+pub fn merge2_test() {
+ let a = uri.parse("http://google.com/weebl")
+ let b = uri.parse("http://example.com/baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/baz")))
+}
- pub fn merge3_test() {
- let a = uri.parse("http://google.com/weebl")
- let b = uri.parse("http://example.com/.././bob/../../baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/baz")))
- }
+pub fn merge3_test() {
+ let a = uri.parse("http://google.com/weebl")
+ let b = uri.parse("http://example.com/.././bob/../../baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/baz")))
+}
- pub fn merge4_test() {
- let a = uri.parse("http://google.com/weebl")
- let b = uri.parse("//example.com/baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/baz")))
- }
+pub fn merge4_test() {
+ let a = uri.parse("http://google.com/weebl")
+ let b = uri.parse("//example.com/baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/baz")))
+}
- pub fn merge5_test() {
- let a = uri.parse("http://google.com/weebl")
- let b = uri.parse("//example.com/.././bob/../../../baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/baz")))
- }
+pub fn merge5_test() {
+ let a = uri.parse("http://google.com/weebl")
+ let b = uri.parse("//example.com/.././bob/../../../baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/baz")))
+}
- pub fn merge6_test() {
- let a = uri.parse("http://example.com/weebl/bob")
- let b = uri.parse("/baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/baz")))
- }
+pub fn merge6_test() {
+ let a = uri.parse("http://example.com/weebl/bob")
+ let b = uri.parse("/baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/baz")))
+}
- pub fn merge7_test() {
- let a = uri.parse("http://example.com/weebl/bob")
- let b = uri.parse("baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/weebl/baz")))
- }
+pub fn merge7_test() {
+ let a = uri.parse("http://example.com/weebl/bob")
+ let b = uri.parse("baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/weebl/baz")))
+}
- pub fn merge8_test() {
- let a = uri.parse("http://example.com/weebl/")
- let b = uri.parse("baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/weebl/baz")))
- }
+pub fn merge8_test() {
+ let a = uri.parse("http://example.com/weebl/")
+ let b = uri.parse("baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/weebl/baz")))
+}
- pub fn merge9_test() {
- let a = uri.parse("http://example.com")
- let b = uri.parse("baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/baz")))
- }
+pub fn merge9_test() {
+ let a = uri.parse("http://example.com")
+ let b = uri.parse("baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/baz")))
+}
- pub fn merge10_test() {
- let a = uri.parse("http://example.com")
- let b = uri.parse("/.././bob/../../../baz")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/baz")))
- }
+pub fn merge10_test() {
+ let a = uri.parse("http://example.com")
+ let b = uri.parse("/.././bob/../../../baz")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/baz")))
+}
- pub fn merge11_test() {
- let a = uri.parse("http://example.com/weebl/bob")
- let b = uri.parse("")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/weebl/bob")))
- }
+pub fn merge11_test() {
+ let a = uri.parse("http://example.com/weebl/bob")
+ let b = uri.parse("")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/weebl/bob")))
+}
- pub fn merge12_test() {
- let a = uri.parse("http://example.com/weebl/bob")
- let b = uri.parse("#fragment")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/weebl/bob#fragment")))
- }
+pub fn merge12_test() {
+ let a = uri.parse("http://example.com/weebl/bob")
+ let b = uri.parse("#fragment")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/weebl/bob#fragment")))
+}
- pub fn merge13_test() {
- let a = uri.parse("http://example.com/weebl/bob")
- let b = uri.parse("?query")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/weebl/bob?query")))
- }
+pub fn merge13_test() {
+ let a = uri.parse("http://example.com/weebl/bob")
+ let b = uri.parse("?query")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/weebl/bob?query")))
+}
- pub fn merge14_test() {
- let a = uri.parse("http://example.com/weebl/bob?query1")
- let b = uri.parse("?query2")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/weebl/bob?query2")))
- }
+pub fn merge14_test() {
+ let a = uri.parse("http://example.com/weebl/bob?query1")
+ let b = uri.parse("?query2")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/weebl/bob?query2")))
+}
- pub fn merge15_test() {
- let a = uri.parse("http://example.com/weebl/bob?query")
- let b = uri.parse("")
- uri.merge(a, b)
- |> should.equal(Ok(uri.parse("http://example.com/weebl/bob?query")))
- }
+pub fn merge15_test() {
+ let a = uri.parse("http://example.com/weebl/bob?query")
+ let b = uri.parse("")
+ uri.merge(a, b)
+ |> should.equal(Ok(uri.parse("http://example.com/weebl/bob?query")))
}