Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

A `FormURLEncoded` datatype represents an ordered list of key-value pairs
with possible duplicates. `encode` function allows to transform `FormURLEncoded`
into a string according to `application/x-www-form-urlencoded`.
into a `Maybe String` according to `application/x-www-form-urlencoded`.
`decode` function transforms a string into a `Maybe FormURLEncoded` structure.

Documentation is available on [Pursuit][Pursuit].

Expand All @@ -23,7 +24,10 @@ Example:
> encode (fromArray [ Tuple "hey" Nothing
> , Tuple "Oh" (Just "Let's go!")
> ])
"hey&Oh=Let's%20go!"
Just "hey&Oh=Let's%20go!"

> decode "a=aa&b=bb"
Just (FormURLEncoded [(Tuple "a" (Just "aa")),(Tuple "b" (Just "bb"))])
```

## Contributing
Expand Down
5 changes: 3 additions & 2 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
"package.json"
],
"dependencies": {
"purescript-globals": "^4.0.0",
"purescript-globals": "^4.1.0",
"purescript-maybe": "^4.0.0",
"purescript-newtype": "^3.0.0",
"purescript-prelude": "^4.0.0",
"purescript-strings": "^4.0.0",
"purescript-tuples": "^5.0.0"
"purescript-tuples": "^5.0.0",
"purescript-foldable-traversable": "^4.1.1"
}
}
23 changes: 17 additions & 6 deletions src/Data/FormURLEncoded.purs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import Prelude

import Data.Maybe (Maybe(..))
import Data.Newtype (class Newtype)
import Data.String (joinWith) as String
import Data.String (joinWith, split) as String
import Data.String.Pattern (Pattern(..))
import Data.Traversable (traverse)
import Data.Tuple (Tuple(..))
import Global.Unsafe (unsafeEncodeURIComponent)
import Global (decodeURIComponent, encodeURIComponent)

-- | `FormURLEncoded` is an ordered list of key-value pairs with possible duplicates.
newtype FormURLEncoded = FormURLEncoded (Array (Tuple String (Maybe String)))
Expand All @@ -29,9 +31,18 @@ instance showFormUrlEncoded :: Show FormURLEncoded where
show (FormURLEncoded kvs) = "(FormURLEncoded " <> show kvs <> ")"

-- | Encode `FormURLEncoded` as `application/x-www-form-urlencoded`.
encode :: FormURLEncoded -> String
encode = String.joinWith "&" <<< map encodePart <<< toArray
encode :: FormURLEncoded -> Maybe String
encode = map (String.joinWith "&") <<< traverse encodePart <<< toArray
where
encodePart = case _ of
Tuple k Nothing -> unsafeEncodeURIComponent k
Tuple k (Just v) -> unsafeEncodeURIComponent k <> "=" <> unsafeEncodeURIComponent v
Tuple k Nothing -> encodeURIComponent k
Tuple k (Just v) -> (\key val -> key <> "=" <> val) <$> encodeURIComponent k <*> encodeURIComponent v

-- | Decode `FormURLEncoded` from `application/x-www-form-urlencoded`.
decode :: String -> Maybe FormURLEncoded
decode = map FormURLEncoded <<< traverse decodePart <<< String.split (Pattern "&")
where
decodePart = String.split (Pattern "=") >>> case _ of
[k, v] -> (\key val -> Tuple key $ Just val) <$> decodeURIComponent k <*> decodeURIComponent v
[k] -> Tuple <$> decodeURIComponent k <*> pure Nothing
_ -> Nothing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not particularly familiar with decodeURIComponent, but is it possible that a key or value could cause an exception? For example, the MDN documentation provides this pair of examples, the first of which ought to succeed and the second of which ought to fail:

decodeURIComponent('JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B');
// "JavaScript_шеллы"

try { 
  var a = decodeURIComponent('%E0%A4%A'); 
} catch(e) { 
  console.error(e); 
}

// URIError: malformed URI sequence

neither of which contains the explicit pattern = or &, and I'm not sure which part of it causes the exception thrown in the second case.

Do you mind clarifying how the decode function avoids exceptions, and handles the same cases that the JavaScript decodeURIComponent function does? (Feel free to correct me if this question doesn't seem pertinent).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the decode function in this library would:

  1. Successfully decode the same cases that the decodeURIComponent() function does
  2. Return Nothing for cases that would cause an exception in decodeURIComponent()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that there are safe versions in -globals, can we update this @nsaunders?