@@ -16,6 +16,7 @@ module Web.Internal.FormUrlEncoded where
1616import Control.Applicative
1717#endif
1818
19+import Control.Arrow ((***))
1920import Control.Monad ((<=<))
2021import qualified Data.ByteString.Lazy as BSL
2122import qualified Data.ByteString.Lazy.Char8 as BSL8
@@ -194,10 +195,14 @@ class ToForm a where
194195 default toForm :: (Generic a, GToForm (Rep a)) => a -> Form
195196 toForm = genericToForm
196197
197−instance ToForm [(Text, Text)] where toForm = fromList
198−instance ToForm (Map Text [Text]) where toForm = Form . Map.filter (not . null)
199198instance ToForm Form where toForm = id
200199
200+instance (ToFormKey k, ToHttpApiData v) => ToForm [(k, v)] where
201+ toForm = fromList . map (toFormKey *** toQueryParam)
202+
203+instance (ToFormKey k, ToHttpApiData v) => ToForm (Map k [v]) where
204+ toForm = Form . Map.fromListWith (<>) . map (toFormKey *** map toQueryParam) . Map.toList
205+
201206-- | A 'Generic'-based implementation of 'toForm'.
202207-- This is used as a default implementation in 'ToForm'.
203208genericToForm :: (Generic a, GToForm (Rep a)) => a -> Form
@@ -235,8 +240,8 @@ instance (Selector s, ToHttpApiData c) => GToForm (M1 S s (K1 i c)) where
235240--
236241-- instance 'FromForm' Person where
237242-- 'fromForm' (Form m) = Person
238−-- '\<$\>' maybe (Left "key \"name\" not found") 'parseQueryParam' (lookup "name" m)
239−-- '\<*\>' maybe (Left "key \"age\" not found") 'parseQueryParam' (lookup "name" m)
243+-- '<$>' maybe (Left "key \"name\" not found") 'parseQueryParam' (lookup "name" m)
244+-- '<*>' maybe (Left "key \"age\" not found") 'parseQueryParam' (lookup "name" m)
240245-- @
241246--
242247-- Instead of manually writing @'FromForm'@ instances you can
@@ -265,9 +270,19 @@ class FromForm a where
265270 => Form -> Either Text a
266271 fromForm = genericFromForm
267272
268−instance FromForm Form where fromForm = return
269−instance FromForm (Map Text [Text]) where fromForm = return . unForm
270−instance FromForm [(Text, Text)] where fromForm = return . toList
273+instance FromForm Form where fromForm = pure
274+
275+instance (FromFormKey k, FromHttpApiData v) => FromForm [(k, v)] where
276+ fromForm = fmap (concatMap (\(k, vs) -> map ((,) k) vs)) . entriesByKey
277+
278+instance (Ord k, FromFormKey k, FromHttpApiData v) => FromForm (Map k [v]) where
279+ fromForm = fmap (Map.fromListWith (<>)) . entriesByKey
280+
281+-- | Parse a 'Form' into a list of entries groupped by key.
282+entriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])]
283+entriesByKey = traverse parseGroup . Map.toList . unForm
284+ where
285+ parseGroup (k, vs) = (,) <$> parseFormKey k <*> traverse parseQueryParam vs
271286
272287-- | A 'Generic'-based implementation of 'fromForm'.
273288-- This is used as a default implementation in 'FromForm'.