git.iko.soy/typeable/http-api-data
on master help sign in
Merge pull request #11 from fizruk/check-bounds-#2
fec05beNickolay Kudasov
README.md+2
@@ -24,6 +24,8 @@ Numbers:
24"45.2"
24"45.2"
25>>> parseUrlPiece "452" :: Either Text Int
25>>> parseUrlPiece "452" :: Either Text Int
26Right 452
26Right 452
27+>>> parseUrlPiece "256" :: Either Text Int8
28+Left "out of bounds: `256' (should be between -128 and 127)"
27```
29```
28
30
29Strings:
31Strings:
Web/HttpApiData.hs+24−10
@@ -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 #-}
5-- |
6-- |
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 <> "'")
102
103
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
108+ if (n > h || n < l)
109+ then Left ("out of bounds: `" <> input <> "' (should be between " <> T.pack (show l) <> " and " <> T.pack (show h) <> ")")
110+ else Right (fromInteger n)
111+ where
112+ l = toInteger (minBound :: a)
113+ h = toInteger (maxBound :: a)
114+
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
155-- "45.2"
167-- "45.2"
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)"
158--
172--
159-- Strings:
173-- Strings:
160--
174--
test/main.hs+6−2
@@ -56,6 +56,10 @@ spec = do
56 prop "toPathMultiPiece <=> fromPathMultiPiece String" $ \(p::[String]) -> (toPathMultiPiece <=> fromPathMultiPiece) p
56 prop "toPathMultiPiece <=> fromPathMultiPiece String" $ \(p::[String]) -> (toPathMultiPiece <=> fromPathMultiPiece) p
57 prop "toPathMultiPiece <=> fromPathMultiPiece Text" $ \(p::[T.Text]) -> (toPathMultiPiece <=> fromPathMultiPiece) p
57 prop "toPathMultiPiece <=> fromPathMultiPiece Text" $ \(p::[T.Text]) -> (toPathMultiPiece <=> fromPathMultiPiece) p
58
58
59− it "bad ints are rejected" $ fromPathPiece (T.pack "123hello")
59+ it "bad integers are rejected" $ do
60− `shouldBe` (Nothing :: Maybe Int)
60+ fromPathPiece (T.pack "123hello") `shouldBe` (Nothing :: Maybe Int)
61+
62+ it "bounds checking works" $ do
63+ fromPathPiece (T.pack "256") `shouldBe` (Nothing :: Maybe Int8)
64+ fromPathPiece (T.pack "-10") `shouldBe` (Nothing :: Maybe Word)
61
65