@@ -16,13 +16,14 @@ module Web.HttpApiData (
1616 FromHttpApiData (..),
1717
1818 -- * Utility functions
19− parseMaybeHttpApiData,
20− showUrlPiece,
21− readMaybeUrlPiece,
22− readEitherUrlPiece,
19+ parseMaybeTextData,
20+ showTextData,
21+ readMaybeTextData,
22+ readEitherTextData,
2323) where
2424
2525import Control.Applicative
26+import Control.Arrow ((&&&))
2627
2728import Data.Monoid
2829import Data.ByteString (ByteString)
@@ -87,23 +88,47 @@ defaultParseError :: Text -> Either Text a
8788defaultParseError input = Left ("could not convert: `" <> input <> "'")
8889
8990-- | Convert @'Maybe'@ parser into @'Either' 'Text'@ parser with default error message.
90−parseMaybeHttpApiData :: (Text -> Maybe a) -> (Text -> Either Text a)
91−parseMaybeHttpApiData parse input =
91+parseMaybeTextData :: (Text -> Maybe a) -> (Text -> Either Text a)
92+parseMaybeTextData parse input =
9293 case parse input of
9394 Nothing -> defaultParseError input
9495 Just val -> Right val
9596
9697-- | Convert to URL piece using @'Show'@ instance.
97−showUrlPiece :: Show a => a -> Text
98−showUrlPiece = T.pack . show
98+showTextData :: Show a => a -> Text
99+showTextData = T.toLower . T.pack . show
100+
101+-- | Parse given text case insensitive and return the rest of the input.
102+--
103+-- >>> parseUrlPieceWithPrefix "Just " "just 10" :: Either Text Int
104+-- Right 10
105+-- >>> parseUrlPieceWithPrefix "Left " "left" :: Either Text Bool
106+-- Left "could not convert: `left'"
107+parseUrlPieceWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a
108+parseUrlPieceWithPrefix pattern input
109+ | T.toLower pattern == T.toLower prefix = parseUrlPiece rest
110+ | otherwise = defaultParseError input
111+ where
112+ (prefix, rest) = T.splitAt (T.length pattern) input
113+
114+-- | Parse values case insensitively based on @'Show'@ instance.
115+--
116+-- >>> parseBoundedCaseInsensitiveTextData "true" :: Either Text Bool
117+-- Right True
118+-- >>> parseBoundedCaseInsensitiveTextData "FALSE" :: Either Text Bool
119+-- Right False
120+parseBoundedCaseInsensitiveTextData :: forall a. (Show a, Bounded a, Enum a) => Text -> Either Text a
121+parseBoundedCaseInsensitiveTextData = parseMaybeTextData (flip lookup values . T.toLower)
122+ where
123+ values = map (showTextData &&& id) [minBound..maxBound :: a]
99124
100125-- | Parse URL piece using @'Read'@ instance.
101−readMaybeUrlPiece :: Read a => Text -> Maybe a
102−readMaybeUrlPiece = readMaybe . T.unpack
126+readMaybeTextData :: Read a => Text -> Maybe a
127+readMaybeTextData = readMaybe . T.unpack
103128
104129-- | Parse URL piece using @'Read'@ instance.
105−readEitherUrlPiece :: Read a => Text -> Either Text a
106−readEitherUrlPiece = parseMaybeHttpApiData readMaybeUrlPiece
130+readEitherTextData :: Read a => Text -> Either Text a
131+readEitherTextData = parseMaybeTextData readMaybeTextData
107132
108133-- | Run @'Reader'@ as HTTP API data parser.
109134runReader :: Reader a -> Text -> Either Text a
@@ -144,25 +169,25 @@ instance ToHttpApiData Void where
144169 toUrlPiece = absurd
145170#endif
146171
147−instance ToHttpApiData Bool where toUrlPiece = showUrlPiece
148−instance ToHttpApiData Ordering where toUrlPiece = showUrlPiece
149−instance ToHttpApiData Double where toUrlPiece = showUrlPiece
150−instance ToHttpApiData Float where toUrlPiece = showUrlPiece
151−instance ToHttpApiData Int where toUrlPiece = showUrlPiece
152−instance ToHttpApiData Int8 where toUrlPiece = showUrlPiece
153−instance ToHttpApiData Int16 where toUrlPiece = showUrlPiece
154−instance ToHttpApiData Int32 where toUrlPiece = showUrlPiece
155−instance ToHttpApiData Int64 where toUrlPiece = showUrlPiece
156−instance ToHttpApiData Integer where toUrlPiece = showUrlPiece
157−instance ToHttpApiData Word where toUrlPiece = showUrlPiece
158−instance ToHttpApiData Word8 where toUrlPiece = showUrlPiece
159−instance ToHttpApiData Word16 where toUrlPiece = showUrlPiece
160−instance ToHttpApiData Word32 where toUrlPiece = showUrlPiece
161−instance ToHttpApiData Word64 where toUrlPiece = showUrlPiece
172+instance ToHttpApiData Bool where toUrlPiece = showTextData
173+instance ToHttpApiData Ordering where toUrlPiece = showTextData
174+instance ToHttpApiData Double where toUrlPiece = showTextData
175+instance ToHttpApiData Float where toUrlPiece = showTextData
176+instance ToHttpApiData Int where toUrlPiece = showTextData
177+instance ToHttpApiData Int8 where toUrlPiece = showTextData
178+instance ToHttpApiData Int16 where toUrlPiece = showTextData
179+instance ToHttpApiData Int32 where toUrlPiece = showTextData
180+instance ToHttpApiData Int64 where toUrlPiece = showTextData
181+instance ToHttpApiData Integer where toUrlPiece = showTextData
182+instance ToHttpApiData Word where toUrlPiece = showTextData
183+instance ToHttpApiData Word8 where toUrlPiece = showTextData
184+instance ToHttpApiData Word16 where toUrlPiece = showTextData
185+instance ToHttpApiData Word32 where toUrlPiece = showTextData
186+instance ToHttpApiData Word64 where toUrlPiece = showTextData
162187instance ToHttpApiData String where toUrlPiece = T.pack
163188instance ToHttpApiData Text where toUrlPiece = id
164189instance ToHttpApiData L.Text where toUrlPiece = L.toStrict
165−instance ToHttpApiData Day where toUrlPiece = showUrlPiece
190+instance ToHttpApiData Day where toUrlPiece = showTextData
166191
167192instance ToHttpApiData All where toUrlPiece = toUrlPiece . getAll
168193instance ToHttpApiData Any where toUrlPiece = toUrlPiece . getAny
@@ -175,19 +200,19 @@ instance ToHttpApiData a => ToHttpApiData (Last a) where toUrlPiece = toUrlPi
175200
176201-- |
177202-- >>> toUrlPiece (Just "Hello")
178−-- "Just Hello"
203+-- "just Hello"
179204instance ToHttpApiData a => ToHttpApiData (Maybe a) where
180− toUrlPiece (Just x) = "Just " <> toUrlPiece x
181− toUrlPiece Nothing = "Nothing"
205+ toUrlPiece (Just x) = "just " <> toUrlPiece x
206+ toUrlPiece Nothing = "nothing"
182207
183208-- |
184209-- >>> toUrlPiece (Left "err" :: Either String Int)
185−-- "Left err"
210+-- "left err"
186211-- >>> toUrlPiece (Right 3 :: Either String Int)
187−-- "Right 3"
212+-- "right 3"
188213instance (ToHttpApiData a, ToHttpApiData b) => ToHttpApiData (Either a b) where
189− toUrlPiece (Left x) = "Left " <> toUrlPiece x
190− toUrlPiece (Right x) = "Right " <> toUrlPiece x
214+ toUrlPiece (Left x) = "left " <> toUrlPiece x
215+ toUrlPiece (Right x) = "right " <> toUrlPiece x
191216
192217-- |
193218-- >>> parseUrlPiece "_" :: Either Text ()
@@ -216,8 +241,8 @@ instance FromHttpApiData Void where
216241 parseUrlPiece _ = Left "Void cannot be parsed!"
217242#endif
218243
219−instance FromHttpApiData Bool where parseUrlPiece = readEitherUrlPiece
220−instance FromHttpApiData Ordering where parseUrlPiece = readEitherUrlPiece
244+instance FromHttpApiData Bool where parseUrlPiece = parseBoundedCaseInsensitiveTextData
245+instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedCaseInsensitiveTextData
221246instance FromHttpApiData Double where parseUrlPiece = runReader rational
222247instance FromHttpApiData Float where parseUrlPiece = runReader rational
223248instance FromHttpApiData Int where parseUrlPiece = parseBounded (signed decimal)
@@ -234,7 +259,7 @@ instance FromHttpApiData Word64 where parseUrlPiece = parseBounded decimal
234259instance FromHttpApiData String where parseUrlPiece = Right . T.unpack
235260instance FromHttpApiData Text where parseUrlPiece = Right
236261instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
237−instance FromHttpApiData Day where parseUrlPiece = readEitherUrlPiece
262+instance FromHttpApiData Day where parseUrlPiece = readEitherTextData
238263
239264instance FromHttpApiData All where parseUrlPiece = fmap All . parseUrlPiece
240265instance FromHttpApiData Any where parseUrlPiece = fmap Any . parseUrlPiece
@@ -249,30 +274,29 @@ instance FromHttpApiData a => FromHttpApiData (Last a) where parseUrlPiece =
249274-- >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
250275-- Right (Just 123)
251276instance FromHttpApiData a => FromHttpApiData (Maybe a) where
252− parseUrlPiece "Nothing" = return Nothing
253− parseUrlPiece s =
254− case T.stripPrefix "Just " s of
255− Nothing -> defaultParseError s
256− Just x -> Just <$> parseUrlPiece x
277+ parseUrlPiece s
278+ | T.toLower (T.take 7 s) == "nothing" = return Nothing
279+ | otherwise = Just <$> parseUrlPieceWithPrefix "Just " s
257280
258281-- |
259282-- >>> parseUrlPiece "Right 123" :: Either Text (Either String Int)
260283-- Right (Right 123)
261284instance (FromHttpApiData a, FromHttpApiData b) => FromHttpApiData (Either a b) where
262285 parseUrlPiece s =
263− case T.stripPrefix "Right " s of
264− Just r -> Right <$> parseUrlPiece r
265− _ -> case T.stripPrefix "Left " s of
266− Just l -> Left <$> parseUrlPiece l
267− _ -> defaultParseError s
286+ Right <$> parseUrlPieceWithPrefix "Right " s
287+ <!> Left <$> parseUrlPieceWithPrefix "Left " s
288+ where
289+ infixl 3 <!>
290+ Left _ <!> y = y
291+ x <!> _ = x
268292
269293-- $examples
270294--
271295-- Booleans:
272296--
273297-- >>> toUrlPiece True
274−-- "True"
275−-- >>> parseUrlPiece "False" :: Either Text Bool
298+-- "true"
299+-- >>> parseUrlPiece "false" :: Either Text Bool
276300-- Right False
277301-- >>> parseUrlPiece "something else" :: Either Text Bool
278302-- Left "could not convert: `something else'"