@@ -44,6 +44,9 @@ import TextShow (TextShow, showt)
4444#endif
4545
4646-- | Convert value to HTTP API data.
47+--
48+-- __WARNING__: Do not derive this using @DeriveAnyClass@ as the generated
49+-- instance will loop indefinitely.
4750class ToHttpApiData a where
4851 {-# MINIMAL toUrlPiece | toQueryParam #-}
4952 -- | Convert to URL path piece.
@@ -59,6 +62,9 @@ class ToHttpApiData a where
5962 toQueryParam = toUrlPiece
6063
6164-- | Parse value from HTTP API data.
65+--
66+-- __WARNING__: Do not derive this using @DeriveAnyClass@ as the generated
67+-- instance will loop indefinitely.
6268class FromHttpApiData a where
6369 {-# MINIMAL parseUrlPiece | parseQueryParam #-}
6470 -- | Parse URL path piece.
@@ -280,9 +286,53 @@ parseBoundedTextData :: (TextShow a, Bounded a, Enum a) => Text -> Either Text a
280286-- Right Foo
281287parseBoundedTextData :: (Show a, Bounded a, Enum a) => Text -> Either Text a
282288#endif
283−parseBoundedTextData = parseMaybeTextData (flip lookup values . T.toLower)
284− where
285− values = map (showTextData &&& id) [minBound..maxBound]
289+parseBoundedTextData = parseBoundedEnumOfI showTextData
290+
291+-- | Lookup values based on a precalculated mapping of their representations.
292+lookupBoundedEnumOf :: (Bounded a, Enum a, Eq b) => (a -> b) -> b -> Maybe a
293+lookupBoundedEnumOf f = flip lookup (map (f &&& id) [minBound..maxBound])
294+
295+-- | Parse values based on a precalculated mapping of their @'Text'@ representation.
296+--
297+-- >>> parseBoundedEnumOf toUrlPiece "true" :: Either Text Bool
298+-- Right True
299+--
300+-- For case sensitive parser see 'parseBoundedEnumOfI'.
301+parseBoundedEnumOf :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a
302+parseBoundedEnumOf = parseMaybeTextData . lookupBoundedEnumOf
303+
304+-- | /Case insensitive/.
305+--
306+-- Parse values case insensitively based on a precalculated mapping
307+-- of their @'Text'@ representations.
308+--
309+-- >>> parseBoundedEnumOfI toUrlPiece "FALSE" :: Either Text Bool
310+-- Right False
311+--
312+-- For case sensitive parser see 'parseBoundedEnumOf'.
313+parseBoundedEnumOfI :: (Bounded a, Enum a) => (a -> Text) -> Text -> Either Text a
314+parseBoundedEnumOfI f = parseBoundedEnumOf (T.toLower . f) . T.toLower
315+
316+-- | /Case insensitive/.
317+--
318+-- Parse values case insensitively based on @'ToHttpApiData'@ instance.
319+-- Uses @'toUrlPiece'@ to get possible values.
320+parseBoundedUrlPiece :: (ToHttpApiData a, Bounded a, Enum a) => Text -> Either Text a
321+parseBoundedUrlPiece = parseBoundedEnumOfI toUrlPiece
322+
323+-- | /Case insensitive/.
324+--
325+-- Parse values case insensitively based on @'ToHttpApiData'@ instance.
326+-- Uses @'toQueryParam'@ to get possible values.
327+parseBoundedQueryParam :: (ToHttpApiData a, Bounded a, Enum a) => Text -> Either Text a
328+parseBoundedQueryParam = parseBoundedEnumOfI toQueryParam
329+
330+-- | Parse values based on @'ToHttpApiData'@ instance.
331+-- Uses @'toHeader'@ to get possible values.
332+parseBoundedHeader :: (ToHttpApiData a, Bounded a, Enum a) => ByteString -> Either Text a
333+parseBoundedHeader bs = case lookupBoundedEnumOf toHeader bs of
334+ Nothing -> defaultParseError (decodeUtf8 bs)
335+ Just x -> return x
286336
287337-- | Parse URL piece using @'Read'@ instance.
288338--
@@ -442,8 +492,8 @@ instance FromHttpApiData Void where
442492 parseUrlPiece _ = Left "Void cannot be parsed!"
443493#endif
444494
445−instance FromHttpApiData Bool where parseUrlPiece = parseBoundedTextData
446−instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedTextData
495+instance FromHttpApiData Bool where parseUrlPiece = parseBoundedUrlPiece
496+instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedUrlPiece
447497instance FromHttpApiData Double where parseUrlPiece = runReader rational
448498instance FromHttpApiData Float where parseUrlPiece = runReader rational
449499instance FromHttpApiData Int where parseUrlPiece = parseBounded (signed decimal)