git.iko.soy/typeable/http-api-data
on master help sign in
Merge branch 'fierce-katie-bounded-data-#28'
1a961e7Nickolay Kudasov
Web/HttpApiData.hs+8−1
@@ -27,10 +27,17 @@ module Web.HttpApiData (
27 toQueryParams,
27 toQueryParams,
28 parseQueryParams,
28 parseQueryParams,
29
29
30+ -- * Parsers for @'Bounded'@ @'Enum'@s
31+ parseBoundedUrlPiece,
32+ parseBoundedQueryParam,
33+ parseBoundedHeader,
34+ parseBoundedEnumOf,
35+ parseBoundedEnumOfI,
36+ parseBoundedTextData,
37+
30 -- * Other helpers
38 -- * Other helpers
31 showTextData,
39 showTextData,
32 readTextData,
40 readTextData,
33− parseBoundedTextData,
34) where
41) where
35
42
36import Web.HttpApiData.Internal
43import Web.HttpApiData.Internal
Web/HttpApiData/Internal.hs+55−5
@@ -44,6 +44,9 @@ import TextShow (TextShow, showt)
44#endif
44#endif
45
45
46-- | Convert value to HTTP API data.
46-- | Convert value to HTTP API data.
47+--
48+-- __WARNING__: Do not derive this using @DeriveAnyClass@ as the generated
49+-- instance will loop indefinitely.
47class ToHttpApiData a where
50class ToHttpApiData a where
48 {-# MINIMAL toUrlPiece | toQueryParam #-}
51 {-# MINIMAL toUrlPiece | toQueryParam #-}
49 -- | Convert to URL path piece.
52 -- | Convert to URL path piece.
@@ -59,6 +62,9 @@ class ToHttpApiData a where
59 toQueryParam = toUrlPiece
62 toQueryParam = toUrlPiece
60
63
61-- | Parse value from HTTP API data.
64-- | Parse value from HTTP API data.
65+--
66+-- __WARNING__: Do not derive this using @DeriveAnyClass@ as the generated
67+-- instance will loop indefinitely.
62class FromHttpApiData a where
68class FromHttpApiData a where
63 {-# MINIMAL parseUrlPiece | parseQueryParam #-}
69 {-# MINIMAL parseUrlPiece | parseQueryParam #-}
64 -- | Parse URL path piece.
70 -- | Parse URL path piece.
@@ -280,9 +286,53 @@ parseBoundedTextData :: (TextShow a, Bounded a, Enum a) => Text -> Either Text a
280-- Right Foo
286-- Right Foo
281parseBoundedTextData :: (Show a, Bounded a, Enum a) => Text -> Either Text a
287parseBoundedTextData :: (Show a, Bounded a, Enum a) => Text -> Either Text a
282#endif
288#endif
283−parseBoundedTextData = parseMaybeTextData (flip lookup values . T.toLower)
289+parseBoundedTextData = parseBoundedEnumOfI showTextData
284− where
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
285− values = map (showTextData &&& id) [minBound..maxBound]
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
286
336
287-- | Parse URL piece using @'Read'@ instance.
337-- | Parse URL piece using @'Read'@ instance.
288--
338--
@@ -442,8 +492,8 @@ instance FromHttpApiData Void where
442 parseUrlPiece _ = Left "Void cannot be parsed!"
492 parseUrlPiece _ = Left "Void cannot be parsed!"
443#endif
493#endif
444
494
445−instance FromHttpApiData Bool where parseUrlPiece = parseBoundedTextData
495+instance FromHttpApiData Bool where parseUrlPiece = parseBoundedUrlPiece
446−instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedTextData
496+instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedUrlPiece
447instance FromHttpApiData Double where parseUrlPiece = runReader rational
497instance FromHttpApiData Double where parseUrlPiece = runReader rational
448instance FromHttpApiData Float where parseUrlPiece = runReader rational
498instance FromHttpApiData Float where parseUrlPiece = runReader rational
449instance FromHttpApiData Int where parseUrlPiece = parseBounded (signed decimal)
499instance FromHttpApiData Int where parseUrlPiece = parseBounded (signed decimal)