@@ -13,6 +13,7 @@
13{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeFamilies #-}
14{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE TypeOperators #-}
15{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
16+#include "overlapping-compat.h"
16module Web.Internal.FormUrlEncoded where
17module Web.Internal.FormUrlEncoded where
18#if __GLASGOW_HASKELL__ < 710
19#if __GLASGOW_HASKELL__ < 710
@@ -288,7 +289,25 @@ instance (GToForm t f) => GToForm t (M1 D x f) where
288instance (GToForm t f) => GToForm t (M1 C x f) where
289instance (GToForm t f) => GToForm t (M1 C x f) where
289 gToForm p opts (M1 a) = gToForm p opts a
290 gToForm p opts (M1 a) = gToForm p opts a
292+instance OVERLAPPABLE_ (Selector s, ToHttpApiData c) => GToForm t (M1 S s (K1 i c)) where
293+ gToForm _ opts (M1 (K1 c)) = fromList [(key, toQueryParam c)]
295+ key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
291−instance (Selector s, ToHttpApiData c) => GToForm t (M1 S s (K1 i c)) where
297+instance (Selector s, ToHttpApiData c) => GToForm t (M1 S s (K1 i (Maybe c))) where
298+ gToForm _ opts (M1 (K1 c)) =
301+ Just x -> fromList [(key, toQueryParam x)]
303+ key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
305+instance (Selector s, ToHttpApiData c) => GToForm t (M1 S s (K1 i [c])) where
306+ gToForm _ opts (M1 (K1 cs)) = fromList (map (\c -> (key, toQueryParam c)) cs)
308+ key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
310+instance OVERLAPPING_ (Selector s) => GToForm t (M1 S s (K1 i String)) where
292 gToForm _ opts (M1 (K1 c)) = fromList [(key, toQueryParam c)]
311 gToForm _ opts (M1 (K1 c)) = fromList [(key, toQueryParam c)]
294 key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
313 key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
@@ -379,17 +398,32 @@ class GFromForm t (f :: * -> *) where
379instance (GFromForm t f, GFromForm t g) => GFromForm t (f :*: g) where
398instance (GFromForm t f, GFromForm t g) => GFromForm t (f :*: g) where
380 gFromForm p opts f = (:*:) <$> gFromForm p opts f <*> gFromForm p opts f
399 gFromForm p opts f = (:*:) <$> gFromForm p opts f <*> gFromForm p opts f
382−instance (Selector s, FromHttpApiData f) => GFromForm t (M1 S s (K1 i f)) where
383− gFromForm _ opts form = M1 . K1 <$> parseUnique key form
385− key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
387instance GFromForm t f => GFromForm t (M1 D x f) where
401instance GFromForm t f => GFromForm t (M1 D x f) where
388 gFromForm p opts f = M1 <$> gFromForm p opts f
402 gFromForm p opts f = M1 <$> gFromForm p opts f
390instance GFromForm t f => GFromForm t (M1 C x f) where
404instance GFromForm t f => GFromForm t (M1 C x f) where
391 gFromForm p opts f = M1 <$> gFromForm p opts f
405 gFromForm p opts f = M1 <$> gFromForm p opts f
407+instance OVERLAPPABLE_ (Selector s, FromHttpApiData c) => GFromForm t (M1 S s (K1 i c)) where
408+ gFromForm _ opts form = M1 . K1 <$> parseUnique key form
410+ key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
412+instance (Selector s, FromHttpApiData c) => GFromForm t (M1 S s (K1 i (Maybe c))) where
413+ gFromForm _ opts form = M1 . K1 <$> parseMaybe key form
415+ key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
417+instance (Selector s, FromHttpApiData c) => GFromForm t (M1 S s (K1 i [c])) where
418+ gFromForm _ opts form = M1 . K1 <$> parseAll key form
420+ key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
422+instance OVERLAPPING_ (Selector s) => GFromForm t (M1 S s (K1 i String)) where
423+ gFromForm _ opts form = M1 . K1 <$> parseUnique key form
425+ key = Text.pack $ fieldLabelModifier opts $ selName (Proxy3 :: Proxy3 s g p)
393instance NotSupported FromForm t "is a sum type" => GFromForm t (f :+: g) where gFromForm = error "impossible"
427instance NotSupported FromForm t "is a sum type" => GFromForm t (f :+: g) where gFromForm = error "impossible"
395-- | Encode a 'Form' to an @application/x-www-form-urlencoded@ 'BSL.ByteString'.
429-- | Encode a 'Form' to an @application/x-www-form-urlencoded@ 'BSL.ByteString'.
@@ -505,6 +539,22 @@ urlEncodeAsForm = urlEncodeForm . toForm
505lookupAll :: Text -> Form -> [Text]
539lookupAll :: Text -> Form -> [Text]
506lookupAll key = F.concat . HashMap.lookup key . unForm
540lookupAll key = F.concat . HashMap.lookup key . unForm
542+-- | Lookup an optional value for a key.
543+-- Fail if there is more than one value.
545+-- >>> lookupMaybe "name" []
547+-- >>> lookupMaybe "name" [("name", "Oleg")]
548+-- Right (Just "Oleg")
549+-- >>> lookupMaybe "name" [("name", "Oleg"), ("name", "David")]
550+-- Left "Duplicate key \"name\""
551+lookupMaybe :: Text -> Form -> Either Text (Maybe Text)
552+lookupMaybe key form =
553+ case lookupAll key form of
555+ [v] -> pure (Just v)
556+ _ -> Left $ "Duplicate key " <> Text.pack (show key)
508-- | Lookup a unique value for a key.
558-- | Lookup a unique value for a key.
509-- Fail if there is zero or more than one value.
559-- Fail if there is zero or more than one value.
@@ -515,11 +565,11 @@ lookupAll key = F.concat . HashMap.lookup key . unForm
515-- >>> lookupUnique "name" [("name", "Oleg"), ("name", "David")]
565-- >>> lookupUnique "name" [("name", "Oleg"), ("name", "David")]
516-- Left "Duplicate key \"name\""
566-- Left "Duplicate key \"name\""
517lookupUnique :: Text -> Form -> Either Text Text
567lookupUnique :: Text -> Form -> Either Text Text
518−lookupUnique key form =
568+lookupUnique key form = do
569+ mv <- lookupMaybe key form
519− case lookupAll key form of
570+ case mv of
520− [v] -> pure v
571+ Just v -> pure v
521− [] -> Left $ "Could not find key " <> Text.pack (show key)
572+ Nothing -> Left $ "Could not find key " <> Text.pack (show key)
522− _ -> Left $ "Duplicate key " <> Text.pack (show key)
524-- | Lookup all values for a given key in a 'Form' and parse them with 'parseQueryParams'.
574-- | Lookup all values for a given key in a 'Form' and parse them with 'parseQueryParams'.
@@ -534,6 +584,22 @@ lookupUnique key form =
534parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v]
584parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v]
535parseAll key = parseQueryParams . lookupAll key
585parseAll key = parseQueryParams . lookupAll key
587+-- | Lookup an optional value for a given key and parse it with 'parseQueryParam'.
588+-- Fail if there is more than one value for the key.
590+-- >>> parseMaybe "age" [] :: Either Text (Maybe Word8)
592+-- >>> parseMaybe "age" [("age", "12"), ("age", "25")] :: Either Text (Maybe Word8)
593+-- Left "Duplicate key \"age\""
594+-- >>> parseMaybe "age" [("age", "seven")] :: Either Text (Maybe Word8)
595+-- Left "could not parse: `seven' (input does not start with a digit)"
596+-- >>> parseMaybe "age" [("age", "777")] :: Either Text (Maybe Word8)
597+-- Left "out of bounds: `777' (should be between 0 and 255)"
598+-- >>> parseMaybe "age" [("age", "7")] :: Either Text (Maybe Word8)
600+parseMaybe :: FromHttpApiData v => Text -> Form -> Either Text (Maybe v)
601+parseMaybe key = parseQueryParams <=< lookupMaybe key
537-- | Lookup a unique value for a given key and parse it with 'parseQueryParam'.
603-- | Lookup a unique value for a given key and parse it with 'parseQueryParam'.
538-- Fail if there is zero or more than one value for the key.
604-- Fail if there is zero or more than one value for the key.