@@ -233,9 +233,14 @@ instance ToHttpApiData v => ToForm (IntMap [v]) where
233233 toForm = fromEntriesByKey . IntMap.toList
234234
235235-- | Convert a list of entries groupped by key into a 'Form'.
236+--
237+-- >>> fromEntriesByKey [("name",["Nick"]),("color",["red","blue"])]
238+-- fromList [("color","red"),("color","blue"),("name","Nick")]
236239fromEntriesByKey :: (ToFormKey k, ToHttpApiData v) => [(k, [v])] -> Form
237240fromEntriesByKey = Form . HashMap.fromListWith (<>) . map (toFormKey *** map toQueryParam)
238241
242+data Proxy3 a b c = Proxy3
243+
239244-- | A 'Generic'-based implementation of 'toForm'.
240245-- This is used as a default implementation in 'ToForm'.
241246genericToForm :: (Generic a, GToForm (Rep a)) => a -> Form
@@ -314,6 +319,9 @@ instance FromHttpApiData v => FromForm (IntMap [v]) where
314319 fromForm = fmap (IntMap.fromListWith (<>)) . toEntriesByKey
315320
316321-- | Parse a 'Form' into a list of entries groupped by key.
322+--
323+-- >>> toEntriesByKey [("name", "Nick"), ("color", "red"), ("color", "white")] :: Either Text [(Text, [Text])]
324+-- Right [("color",["red","white"]),("name",["Nick"])]
317325toEntriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])]
318326toEntriesByKey = traverse parseGroup . HashMap.toList . unForm
319327 where
@@ -422,8 +430,6 @@ urlDecodeForm bs = toForm <$> traverse parsePair pairs
422430 [k] -> return (k, "")
423431 xs -> Left $ "not a valid pair: " <> Text.intercalate "=" xs
424432
425−data Proxy3 a b c = Proxy3
426−
427433-- | This is a convenience function for decoding a
428434-- @application/x-www-form-urlencoded@ 'BSL.ByteString' directly to a datatype
429435-- that has an instance of 'FromForm'.
@@ -448,14 +454,14 @@ urlEncodeAsForm = urlEncodeForm . toForm
448454
449455-- | Find all values corresponding to a given key in a 'Form'.
450456--
451−-- >>> lookupKey "name" []
457+-- >>> lookupAll "name" []
452458-- []
453−-- >>> lookupKey "name" [("name", "Oleg")]
459+-- >>> lookupAll "name" [("name", "Oleg")]
454460-- ["Oleg"]
455−-- >>> lookupKey "name" [("name", "Oleg"), ("name", "David")]
461+-- >>> lookupAll "name" [("name", "Oleg"), ("name", "David")]
456462-- ["Oleg","David"]
457−lookupKey :: Text -> Form -> [Text]
458−lookupKey key = F.concat . HashMap.lookup key . unForm
463+lookupAll :: Text -> Form -> [Text]
464+lookupAll key = F.concat . HashMap.lookup key . unForm
459465
460466-- | Lookup a unique value for a key.
461467-- Fail if there is zero or more than one value.
@@ -468,12 +474,26 @@ lookupKey key = F.concat . HashMap.lookup key . unForm
468474-- Left "Duplicate key \"name\""
469475lookupUnique :: Text -> Form -> Either Text Text
470476lookupUnique key form =
471− case lookupKey key form of
477+ case lookupAll key form of
472478 [v] -> pure v
473479 [] -> Left $ "Could not find key " <> Text.pack (show key)
474480 _ -> Left $ "Duplicate key " <> Text.pack (show key)
475481
482+-- | Lookup all values for a given key in a 'Form' and parse them with 'parseQueryParams'.
483+--
484+-- >>> parseAll "age" [] :: Either Text [Word8]
485+-- Right []
486+-- >>> parseAll "age" [("age", "8"), ("age", "seven")] :: Either Text [Word8]
487+-- Left "could not parse: `seven' (input does not start with a digit)"
488+-- >>> parseAll "age" [("age", "8"), ("age", "777")] :: Either Text [Word8]
489+-- Left "out of bounds: `777' (should be between 0 and 255)"
490+-- >>> parseAll "age" [("age", "12"), ("age", "25")] :: Either Text [Word8]
491+-- Right [12,25]
492+parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v]
493+parseAll key = parseQueryParams . lookupAll key
494+
476495-- | Lookup a unique value for a given key and parse it with 'parseQueryParam'.
496+-- Fail if there is zero or more than one value for the key.
477497--
478498-- >>> parseUnique "age" [] :: Either Text Word8
479499-- Left "Could not find key \"age\""
@@ -488,16 +508,3 @@ lookupUnique key form =
488508parseUnique :: FromHttpApiData v => Text -> Form -> Either Text v
489509parseUnique key form = lookupUnique key form >>= parseQueryParam
490510
491−-- | Lookup all values for a given key in a 'Form' and parse them with 'parseQueryParams'.
492−--
493−-- >>> parseAll "age" [] :: Either Text [Word8]
494−-- Right []
495−-- >>> parseAll "age" [("age", "8"), ("age", "seven")] :: Either Text [Word8]
496−-- Left "could not parse: `seven' (input does not start with a digit)"
497−-- >>> parseAll "age" [("age", "8"), ("age", "777")] :: Either Text [Word8]
498−-- Left "out of bounds: `777' (should be between 0 and 255)"
499−-- >>> parseAll "age" [("age", "12"), ("age", "25")] :: Either Text [Word8]
500−-- Right [12,25]
501−parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v]
502−parseAll key = parseQueryParams . lookupKey key
503−