git.iko.soy/typeable/http-api-data
on master help sign in
Add bounds checking for bounded integral types
commitdba224c364757426ef17ad49629d6e636e050ec2 authorNickolay Kudasov <nickolay.kudasov@gmail.com> dateMon 5 Oct 2015, 13:13 (11 years ago) parent38815ae
2 files changed+30−12
Web/HttpApiData.hs+24−10 test/main.hs+6−2
Web/HttpApiData.hs+24−10
@@ -1,6 +1,7 @@
11{-# LANGUAGE DefaultSignatures #-}
22{-# LANGUAGE FlexibleInstances #-}
33{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
45{-# LANGUAGE TypeSynonymInstances #-}
56-- |
67-- Convert Haskell values to and from HTTP API data
@@ -100,6 +101,17 @@ runReader reader input =
100101 | T.null rest -> Right x
101102 | otherwise -> Left ("could not convert: `" <> input <> "'")
102103
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+
103115instance ToHttpApiData Bool where toUrlPiece = showUrlPiece
104116instance ToHttpApiData Double where toUrlPiece = showUrlPiece
105117instance ToHttpApiData Float where toUrlPiece = showUrlPiece
@@ -122,17 +134,17 @@ instance ToHttpApiData Day where toUrlPiece = showUrlPiece
122134instance FromHttpApiData Bool where parseUrlPiece = readEitherUrlPiece
123135instance FromHttpApiData Double where parseUrlPiece = runReader rational
124136instance FromHttpApiData Float where parseUrlPiece = runReader rational
125−instance FromHttpApiData Int where parseUrlPiece = runReader (signed decimal)
126−instance FromHttpApiData Int8 where parseUrlPiece = runReader (signed decimal)
127−instance FromHttpApiData Int16 where parseUrlPiece = runReader (signed decimal)
128−instance FromHttpApiData Int32 where parseUrlPiece = runReader (signed decimal)
129−instance FromHttpApiData Int64 where parseUrlPiece = runReader (signed decimal)
137+instance FromHttpApiData Int where parseUrlPiece = parseBounded (signed decimal)
138+instance FromHttpApiData Int8 where parseUrlPiece = parseBounded (signed decimal)
139+instance FromHttpApiData Int16 where parseUrlPiece = parseBounded (signed decimal)
140+instance FromHttpApiData Int32 where parseUrlPiece = parseBounded (signed decimal)
141+instance FromHttpApiData Int64 where parseUrlPiece = parseBounded (signed decimal)
130142instance FromHttpApiData Integer where parseUrlPiece = runReader (signed decimal)
131−instance FromHttpApiData Word where parseUrlPiece = runReader decimal
132−instance FromHttpApiData Word8 where parseUrlPiece = runReader decimal
133−instance FromHttpApiData Word16 where parseUrlPiece = runReader decimal
134−instance FromHttpApiData Word32 where parseUrlPiece = runReader decimal
135−instance FromHttpApiData Word64 where parseUrlPiece = runReader decimal
143+instance FromHttpApiData Word where parseUrlPiece = parseBounded decimal
144+instance FromHttpApiData Word8 where parseUrlPiece = parseBounded decimal
145+instance FromHttpApiData Word16 where parseUrlPiece = parseBounded decimal
146+instance FromHttpApiData Word32 where parseUrlPiece = parseBounded decimal
147+instance FromHttpApiData Word64 where parseUrlPiece = parseBounded decimal
136148instance FromHttpApiData String where parseUrlPiece = Right . T.unpack
137149instance FromHttpApiData Text where parseUrlPiece = Right
138150instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
@@ -155,6 +167,8 @@ instance FromHttpApiData Day where parseUrlPiece = readEitherUrlPiece
155167-- "45.2"
156168-- >>> parseUrlPiece "452" :: Either Text Int
157169-- Right 452
170+-- >>> parseUrlPiece "256" :: Either Text Int8
171+-- Left "out of bounds: `256' (should be between -128 and 127)"
158172--
159173-- Strings:
160174--
test/main.hs+6−2
@@ -56,6 +56,10 @@ spec = do
5656 prop "toPathMultiPiece <=> fromPathMultiPiece String" $ \(p::[String]) -> (toPathMultiPiece <=> fromPathMultiPiece) p
5757 prop "toPathMultiPiece <=> fromPathMultiPiece Text" $ \(p::[T.Text]) -> (toPathMultiPiece <=> fromPathMultiPiece) p
5858
59− it "bad ints are rejected" $ fromPathPiece (T.pack "123hello")
60− `shouldBe` (Nothing :: Maybe Int)
59+ it "bad integers are rejected" $ do
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)
6165