@@ -255,8 +255,8 @@ instance (Selector s, ToHttpApiData c) => GToForm (M1 S s (K1 i c)) where
256-- instance 'FromForm' Person where
256-- instance 'FromForm' Person where
257-- 'fromForm' (Form m) = Person
257-- 'fromForm' (Form m) = Person
258−-- '<$>' maybe (Left "key \"name\" not found") 'parseQueryParam' (lookup "name" m)
258+-- '<$>' 'parseUnique' "name" m
259−-- '<*>' maybe (Left "key \"age\" not found") 'parseQueryParam' (lookup "name" m)
259+-- '<*>' 'parseUnique' "age" m
262-- Instead of manually writing @'FromForm'@ instances you can
262-- Instead of manually writing @'FromForm'@ instances you can
@@ -326,11 +326,7 @@ instance (GFromForm f, GFromForm g) => GFromForm (f :+: g) where
326 x <!> _ = x
326 x <!> _ = x
328instance (Selector s, FromHttpApiData f) => GFromForm (M1 S s (K1 i f)) where
328instance (Selector s, FromHttpApiData f) => GFromForm (M1 S s (K1 i f)) where
329− gFromForm f =
329+ gFromForm form = M1 . K1 <$> parseUnique key form
330− case concat (Map.lookup key (unForm f)) of
331− [v] -> M1 . K1 <$> parseQueryParam v
332− [] -> Left $ "Could not find key " <> Text.pack (show key)
333− _ -> Left $ "Duplicate key " <> Text.pack (show key)
335 key = Text.pack $ selName (Proxy3 :: Proxy3 s g p)
331 key = Text.pack $ selName (Proxy3 :: Proxy3 s g p)
@@ -444,3 +440,59 @@ decodeAsForm = fromForm <=< decodeForm
444-- "age=22&name=Dennis"
440-- "age=22&name=Dennis"
445encodeAsForm :: ToForm a => a -> BSL.ByteString
441encodeAsForm :: ToForm a => a -> BSL.ByteString
446encodeAsForm = encodeForm . toForm
442encodeAsForm = encodeForm . toForm
444+-- | Find all values corresponding to a given key in a 'Form'.
446+-- >>> lookupKey "name" []
448+-- >>> lookupKey "name" [("name", "Oleg")]
450+-- >>> lookupKey "name" [("name", "Oleg"), ("name", "David")]
452+lookupKey :: Text -> Form -> [Text]
453+lookupKey key = concat . Map.lookup key . unForm
455+-- | Lookup a unique value for a key.
456+-- Fail if there is zero or more than one value.
458+-- >>> lookupUnique "name" []
459+-- Left "Could not find key \"name\""
460+-- >>> lookupUnique "name" [("name", "Oleg")]
462+-- >>> lookupUnique "name" [("name", "Oleg"), ("name", "David")]
463+-- Left "Duplicate key \"name\""
464+lookupUnique :: Text -> Form -> Either Text Text
465+lookupUnique key form =
466+ case lookupKey key form of
468+ [] -> Left $ "Could not find key " <> Text.pack (show key)
469+ _ -> Left $ "Duplicate key " <> Text.pack (show key)
471+-- | Lookup a unique value for a given key and parse it with 'parseQueryParam'.
473+-- >>> parseUnique "age" [] :: Either Text Word8
474+-- Left "Could not find key \"age\""
475+-- >>> parseUnique "age" [("age", "12"), ("age", "25")] :: Either Text Word8
476+-- Left "Duplicate key \"age\""
477+-- >>> parseUnique "age" [("age", "seven")] :: Either Text Word8
478+-- Left "could not parse: `seven' (input does not start with a digit)"
479+-- >>> parseUnique "age" [("age", "777")] :: Either Text Word8
480+-- Left "out of bounds: `777' (should be between 0 and 255)"
481+-- >>> parseUnique "age" [("age", "7")] :: Either Text Word8
483+parseUnique :: FromHttpApiData v => Text -> Form -> Either Text v
484+parseUnique key form = lookupUnique key form >>= parseQueryParam
486+-- | Lookup all values for a given key in a 'Form' and parse them with 'parseQueryParams'.
488+-- >>> parseAll "age" [] :: Either Text [Word8]
490+-- >>> parseAll "age" [("age", "8"), ("age", "seven")] :: Either Text [Word8]
491+-- Left "could not parse: `seven' (input does not start with a digit)"
492+-- >>> parseAll "age" [("age", "8"), ("age", "777")] :: Either Text [Word8]
493+-- Left "out of bounds: `777' (should be between 0 and 255)"
494+-- >>> parseAll "age" [("age", "12"), ("age", "25")] :: Either Text [Word8]
496+parseAll :: FromHttpApiData v => Text -> Form -> Either Text [v]
497+parseAll key = parseQueryParams . lookupKey key