@@ -1,6 +1,7 @@
1{-# LANGUAGE DefaultSignatures #-}
1{-# LANGUAGE DefaultSignatures #-}
2{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
4{-# LANGUAGE TypeSynonymInstances #-}
5{-# LANGUAGE TypeSynonymInstances #-}
6-- Convert Haskell values to and from HTTP API data
7-- Convert Haskell values to and from HTTP API data
@@ -100,6 +101,17 @@ runReader reader input =
100 | T.null rest -> Right x
101 | T.null rest -> Right x
101 | otherwise -> Left ("could not convert: `" <> input <> "'")
102 | otherwise -> Left ("could not convert: `" <> input <> "'")
104+-- | Run @'Reader'@ to parse bounded integral value with bounds checking.
105+parseBounded :: forall a. (Bounded a, Integral a) => Reader Integer -> Text -> Either Text a
106+parseBounded reader input = do
107+ n <- runReader reader input
109+ then Left ("out of bounds: `" <> input <> "' (should be between " <> T.pack (show l) <> " and " <> T.pack (show h) <> ")")
110+ else Right (fromInteger n)
112+ l = toInteger (minBound :: a)
113+ h = toInteger (maxBound :: a)
103instance ToHttpApiData Bool where toUrlPiece = showUrlPiece
115instance ToHttpApiData Bool where toUrlPiece = showUrlPiece
104instance ToHttpApiData Double where toUrlPiece = showUrlPiece
116instance ToHttpApiData Double where toUrlPiece = showUrlPiece
105instance ToHttpApiData Float where toUrlPiece = showUrlPiece
117instance ToHttpApiData Float where toUrlPiece = showUrlPiece
@@ -122,17 +134,17 @@ instance ToHttpApiData Day where toUrlPiece = showUrlPiece
122instance FromHttpApiData Bool where parseUrlPiece = readEitherUrlPiece
134instance FromHttpApiData Bool where parseUrlPiece = readEitherUrlPiece
123instance FromHttpApiData Double where parseUrlPiece = runReader rational
135instance FromHttpApiData Double where parseUrlPiece = runReader rational
124instance FromHttpApiData Float where parseUrlPiece = runReader rational
136instance FromHttpApiData Float where parseUrlPiece = runReader rational
125−instance FromHttpApiData Int where parseUrlPiece = runReader (signed decimal)
137+instance FromHttpApiData Int where parseUrlPiece = parseBounded (signed decimal)
126−instance FromHttpApiData Int8 where parseUrlPiece = runReader (signed decimal)
138+instance FromHttpApiData Int8 where parseUrlPiece = parseBounded (signed decimal)
127−instance FromHttpApiData Int16 where parseUrlPiece = runReader (signed decimal)
139+instance FromHttpApiData Int16 where parseUrlPiece = parseBounded (signed decimal)
128−instance FromHttpApiData Int32 where parseUrlPiece = runReader (signed decimal)
140+instance FromHttpApiData Int32 where parseUrlPiece = parseBounded (signed decimal)
129−instance FromHttpApiData Int64 where parseUrlPiece = runReader (signed decimal)
141+instance FromHttpApiData Int64 where parseUrlPiece = parseBounded (signed decimal)
130instance FromHttpApiData Integer where parseUrlPiece = runReader (signed decimal)
142instance FromHttpApiData Integer where parseUrlPiece = runReader (signed decimal)
131−instance FromHttpApiData Word where parseUrlPiece = runReader decimal
143+instance FromHttpApiData Word where parseUrlPiece = parseBounded decimal
132−instance FromHttpApiData Word8 where parseUrlPiece = runReader decimal
144+instance FromHttpApiData Word8 where parseUrlPiece = parseBounded decimal
133−instance FromHttpApiData Word16 where parseUrlPiece = runReader decimal
145+instance FromHttpApiData Word16 where parseUrlPiece = parseBounded decimal
134−instance FromHttpApiData Word32 where parseUrlPiece = runReader decimal
146+instance FromHttpApiData Word32 where parseUrlPiece = parseBounded decimal
135−instance FromHttpApiData Word64 where parseUrlPiece = runReader decimal
147+instance FromHttpApiData Word64 where parseUrlPiece = parseBounded decimal
136instance FromHttpApiData String where parseUrlPiece = Right . T.unpack
148instance FromHttpApiData String where parseUrlPiece = Right . T.unpack
137instance FromHttpApiData Text where parseUrlPiece = Right
149instance FromHttpApiData Text where parseUrlPiece = Right
138instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
150instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
@@ -155,6 +167,8 @@ instance FromHttpApiData Day where parseUrlPiece = readEitherUrlPiece
156-- >>> parseUrlPiece "452" :: Either Text Int
168-- >>> parseUrlPiece "452" :: Either Text Int
157-- Right 452
169-- Right 452
170+-- >>> parseUrlPiece "256" :: Either Text Int8
171+-- Left "out of bounds: `256' (should be between -128 and 127)"
159-- Strings:
173-- Strings: