diff options
author | Louis Pilfold <louis@lpil.uk> | 2021-09-04 23:06:09 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2021-09-04 23:06:09 +0100 |
commit | 7a52f1356fe39475b7576ca5f105a10ae975e7ff (patch) | |
tree | 26aaef910ccba182d38474630bb904af732934f2 | |
parent | a54b1986daf44ebfd941849584175215306ad6d9 (diff) | |
download | gleam_stdlib-7a52f1356fe39475b7576ca5f105a10ae975e7ff.tar.gz gleam_stdlib-7a52f1356fe39475b7576ca5f105a10ae975e7ff.zip |
JS parse query
-rw-r--r-- | src/gleam/uri.gleam | 55 | ||||
-rw-r--r-- | src/gleam_stdlib.erl | 15 | ||||
-rw-r--r-- | src/gleam_stdlib.js | 18 | ||||
-rw-r--r-- | test/gleam/uri_test.gleam | 290 |
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"))) } |