diff options
author | Sebastian Porto <s@porto5.com> | 2020-10-31 22:20:20 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-31 12:20:20 +0100 |
commit | 33fd0fb3130bf1f11947385c42e8bfadb3c753e1 (patch) | |
tree | bb05d3c339787f796add38add8ad932f5c0ee7be | |
parent | 8e421527ad59a4bcea656ca1cdd6c643c99771c9 (diff) | |
download | gleam_stdlib-33fd0fb3130bf1f11947385c42e8bfadb3c753e1.tar.gz gleam_stdlib-33fd0fb3130bf1f11947385c42e8bfadb3c753e1.zip |
Add percent_encode and percent_decode (#121)
-rw-r--r-- | CHANGELOG.md | 3 | ||||
-rw-r--r-- | src/gleam/uri.gleam | 28 | ||||
-rw-r--r-- | test/gleam/uri_test.gleam | 74 |
3 files changed, 104 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index c9c9dfe..06389e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,10 @@ - The `function` module gains `curry2` to `curry6`. - The `list` module gains the `each`, and `partition` functions. - The `int` and `float` modules gain the `negate` function. +- The `int` module gains the `to_float` function. - The `result` module gains the `all` function. - The `dynamic` module gains the `option` function. -- The `int` module gains the `to_float` function. +- The `uri` module gains the `percent_encode` and `percent_decode` functions. ## v0.11.0 - 2020-08-22 diff --git a/src/gleam/uri.gleam b/src/gleam/uri.gleam index 32910b5..2ff542a 100644 --- a/src/gleam/uri.gleam +++ b/src/gleam/uri.gleam @@ -14,6 +14,7 @@ import gleam/string import gleam/dynamic.{Dynamic} import gleam/map.{Map} import gleam/function +import gleam/pair /// Type representing holding the parsed components of an URI. /// All components of a URI are optional, except the path. @@ -119,6 +120,33 @@ pub fn query_to_string(query: List(tuple(String, String))) -> String { |> result.unwrap("") } +/// Encode a string into a percent encoded representation. +/// Note that this encodes space as +. +/// +/// ## Example +/// +/// percent_encode("100% great") +/// > "100%25+great" +/// +pub fn percent_encode(value: String) -> String { + query_to_string([tuple("k", value)]) + |> string.replace(each: "k=", with: "") +} + +/// Decode a percent encoded string. +/// +/// ## Example +/// +/// percent_decode("100%25+great") +/// > Ok("100% great") +/// +pub fn percent_decode(value: String) -> Result(String, Nil) { + string.concat(["k=", value]) + |> parse_query + |> result.then(list.head) + |> result.map(pair.second) +} + fn do_remove_dot_segments( input: List(String), accumulator: List(String), diff --git a/test/gleam/uri_test.gleam b/test/gleam/uri_test.gleam index 55a4bd6..779e078 100644 --- a/test/gleam/uri_test.gleam +++ b/test/gleam/uri_test.gleam @@ -1,5 +1,7 @@ import gleam/uri import gleam/should +import gleam/string +import gleam/list import gleam/option.{None, Some} pub fn full_parse_test() { @@ -92,6 +94,78 @@ pub fn empty_query_to_string_test() { should.equal(query_string, "") } +fn percent_codec_fixtures() { + [ + tuple(" ", "+"), + tuple(",", "%2C"), + tuple(";", "%3B"), + tuple(":", "%3A"), + tuple("!", "%21"), + tuple("?", "%3F"), + tuple("'", "%27"), + tuple("(", "%28"), + tuple(")", "%29"), + tuple("[", "%5B"), + tuple("@", "%40"), + tuple("/", "%2F"), + tuple("\\", "%5C"), + tuple("&", "%26"), + tuple("#", "%23"), + tuple("=", "%3D"), + tuple("~", "%7E"), + tuple("ñ", "%C3%B1"), + // Allowed chars + tuple("-", "-"), + tuple("_", "_"), + tuple(".", "."), + tuple("*", "*"), + tuple("100% great", "100%25+great"), + ] +} + +pub fn percent_encode_test() { + percent_codec_fixtures() + |> list.map(fn(t) { + let tuple(a, b) = t + uri.percent_encode(a) + |> should.equal(b) + }) +} + +pub fn percent_encode_consistency_test() { + let k = "foo bar[]" + let v = "ñaña (,:*~)" + + assert query_string = uri.query_to_string([tuple(k, v)]) + + let encoded_key = uri.percent_encode(k) + let encoded_value = uri.percent_encode(v) + let manual_query_string = string.concat([encoded_key, "=", encoded_value]) + + should.equal(query_string, manual_query_string) +} + +pub fn percent_decode_test() { + percent_codec_fixtures() + |> list.map(fn(t) { + let tuple(a, b) = t + uri.percent_decode(b) + |> should.equal(Ok(a)) + }) +} + +pub fn percent_decode_consistency_test() { + let k = "foo+bar[]" + let v = "%C3%B6rebro" + let query = string.concat([k, "=", v]) + assert Ok(parsed) = uri.parse_query(query) + + assert Ok(decoded_key) = uri.percent_decode(k) + assert Ok(decoded_value) = uri.percent_decode(v) + + should.equal(parsed, [tuple(decoded_key, decoded_value)]) +} + pub fn parse_segments_test() { should.equal(uri.path_segments("/"), []) should.equal(uri.path_segments("/foo/bar"), ["foo", "bar"]) |