From 5edccd3dd8f4ea41c6f5900a5c2afb1641145f9d Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Wed, 16 Dec 2020 08:51:04 -0800 Subject: [PATCH] Implement encoding and decoding functions (#1) --- spago.dhall | 2 +- src/JSURI.js | 41 +++++++++++++++++++++++ src/JSURI.purs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/Main.purs | 10 ------ test/Main.purs | 13 ++++++-- 5 files changed, 141 insertions(+), 14 deletions(-) create mode 100644 src/JSURI.js create mode 100644 src/JSURI.purs delete mode 100644 src/Main.purs diff --git a/spago.dhall b/spago.dhall index 6c8b160..9ceec7d 100644 --- a/spago.dhall +++ b/spago.dhall @@ -1,5 +1,5 @@ { name = "js-uri" -, dependencies = [ "console", "effect", "psci-support" ] +, dependencies = [ "assert", "effect", "functions", "maybe", "psci-support" ] , packages = ./packages.dhall , sources = [ "src/**/*.purs", "test/**/*.purs" ] } diff --git a/src/JSURI.js b/src/JSURI.js new file mode 100644 index 0000000..8c7cd6b --- /dev/null +++ b/src/JSURI.js @@ -0,0 +1,41 @@ +"use strict"; + +// A helper which transforms the result ofencodeURIComponent to be compliant +// with RFC3896, as described in the MDN documentation here: +// +// https://web.archive.org/web/20201206001047/https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent +function toRFC3896(input) { + return input.replace(/[!'()*]/g, function (c) { + return "%" + c.charCodeAt(0).toString(16); + }); +} + +exports._encodeURIComponent = function encode(fail, succeed, input) { + try { + return succeed(toRFC3896(encodeURIComponent(input))); + } catch (err) { + return fail(err); + } +}; + +exports._encodeFormURLComponent = function encode(fail, succeed, input) { + try { + return succeed(toRFC3896(encodeURIComponent(input)).replace(/%20/g, "+")); + } catch (err) { + return fail(err); + } +}; + +function _decodeURIComponent(fail, succeed, input) { + try { + return succeed(decodeURIComponent(input)); + } catch (err) { + return fail(err); + } +} + +exports._decodeURIComponent = _decodeURIComponent; + +exports._decodeFormURLComponent = function encode(fail, succeed, input) { + return _decodeURIComponent(fail, succeed, input.replace(/\+/g, " ")); +}; diff --git a/src/JSURI.purs b/src/JSURI.purs new file mode 100644 index 0000000..1364406 --- /dev/null +++ b/src/JSURI.purs @@ -0,0 +1,89 @@ +module JSURI + ( encodeURIComponent + , encodeFormURLComponent + , decodeURIComponent + , decodeFormURLComponent + ) where + +import Prelude + +import Data.Function.Uncurried (Fn3, runFn3) +import Data.Maybe (Maybe(..)) + +foreign import _encodeURIComponent :: Fn3 (String -> Maybe String) (String -> Maybe String) String (Maybe String) + +-- | URI-encode a string according to RFC3896. Implemented using JavaScript's +-- | `encodeURIComponent`. +-- | +-- | ```purs +-- | > encodeURIComponent "https://purescript.org" +-- | Just "https%3A%2F%2Fpurescript.org" +-- | ``` +-- | +-- | Encoding a URI can fail with a `URIError` if the string contains malformed +-- | characters. If you are confident you are encoding a well-formed string then +-- | you can run this function unsafely: +-- | +-- | ```purs +-- | import Partial.Unsafe (unsafePartial) +-- | import Data.Maybe (fromJust) +-- | +-- | unsafeEncode :: String -> String +-- | unsafeEncode str = unsafePartial $ fromJust $ encodeURIComponent str +-- | ``` +encodeURIComponent :: String -> Maybe String +encodeURIComponent = runFn3 _encodeURIComponent (const Nothing) Just + +foreign import _encodeFormURLComponent :: Fn3 (String -> Maybe String) (String -> Maybe String) String (Maybe String) + +-- | URI-encode a string according to RFC3896, except with spaces encoded using +-- | '+' instead of '%20' to comply with application/x-www-form-urlencoded. +-- | +-- | ```purs +-- | > encodeURIComponent "abc ABC" +-- | Just "abc%20ABC" +-- | +-- | > encodeFormURLComponent "abc ABC" +-- | Just "abc+ABC" +-- | ``` +encodeFormURLComponent :: String -> Maybe String +encodeFormURLComponent = runFn3 _encodeFormURLComponent (const Nothing) Just + +foreign import _decodeURIComponent :: Fn3 (String -> Maybe String) (String -> Maybe String) String (Maybe String) + +-- | Decode a URI string according to RFC3896. Implemented using JavaScript's +-- | `decodeURIComponent`. +-- | +-- | ```purs +-- | > decodeURIComponent "https%3A%2F%2Fpurescript.org" +-- | Just "https://purescript.org" +-- | ``` +-- | +-- | Decoding a URI can fail with a `URIError` if the string contains malformed +-- | characters. If you are confident you are encoding a well-formed string then +-- | you can run this function unsafely: +-- | +-- | ```purs +-- | import Partial.Unsafe (unsafePartial) +-- | import Data.Maybe (fromJust) +-- | +-- | unsafeDecode :: String -> String +-- | unsafeDecode str = unsafePartial $ fromJust $ decodeURIComponent str +-- | ``` +decodeURIComponent :: String -> Maybe String +decodeURIComponent = runFn3 _decodeURIComponent (const Nothing) Just + +foreign import _decodeFormURLComponent :: Fn3 (String -> Maybe String) (String -> Maybe String) String (Maybe String) + +-- | Decode a URI according to application/x-www-form-urlencoded (for example, +-- | a string containing '+' for spaces or query parameters). +-- | +-- | ```purs +-- | > decodeURIComponent "https%3A%2F%2Fpurescript.org?search+query" +-- | Just "https://purescript.org?search+query" +-- | +-- | > decodeFormURLComponent "https%3A%2F%2Fpurescript.org?search+query" +-- | Just "https://purescript.org?search query" +-- | ``` +decodeFormURLComponent :: String -> Maybe String +decodeFormURLComponent = runFn3 _decodeFormURLComponent (const Nothing) Just diff --git a/src/Main.purs b/src/Main.purs deleted file mode 100644 index 5c18dca..0000000 --- a/src/Main.purs +++ /dev/null @@ -1,10 +0,0 @@ -module Main where - -import Prelude - -import Effect (Effect) -import Effect.Console (log) - -main :: Effect Unit -main = do - log "🍝" diff --git a/test/Main.purs b/test/Main.purs index f91f98c..f28c23a 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -2,10 +2,17 @@ module Test.Main where import Prelude +import Data.Maybe (Maybe(..)) import Effect (Effect) -import Effect.Class.Console (log) +import JSURI (decodeFormURLComponent, decodeURIComponent, encodeFormURLComponent, encodeURIComponent) +import Test.Assert (assert) main :: Effect Unit main = do - log "🍝" - log "You should add some tests." + assert $ encodeURIComponent "https://purescript.org" == Just "https%3A%2F%2Fpurescript.org" + assert $ encodeURIComponent "abc ABC" == Just "abc%20ABC" + assert $ encodeFormURLComponent "abc ABC" == Just "abc+ABC" + + assert $ decodeURIComponent "https%3A%2F%2Fpurescript.org" == Just "https://purescript.org" + assert $ decodeURIComponent "https%3A%2F%2Fpurescript.org?search+query" == Just "https://purescript.org?search+query" + assert $ decodeFormURLComponent "https%3A%2F%2Fpurescript.org?search+query" == Just "https://purescript.org?search query"