git.iko.soy/typeable/http-api-data
on master help sign in
Move Web.HttpApiData content to .Internal module and export only classes
commita00dcb69aa1d3da3728734e859248f76a5759bd0 authorNickolay Kudasov <nickolay.kudasov@gmail.com> dateWed 7 Oct 2015, 12:23 (11 years ago) parentded768b
4 files changed+280−273
Web/HttpApiData.hs+5−273 Web/HttpApiData/Internal.hs+273 Web/PathPieces.hs+1 http-api-data.cabal+1
Web/HttpApiData.hs+5−273
@@ -1,9 +1,3 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DefaultSignatures #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE OverloadedStrings #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−{-# LANGUAGE TypeSynonymInstances #-}
71-- |
82-- Convert Haskell values to and from HTTP API data
93-- such as URL pieces, headers and query parameters.
@@ -14,281 +8,19 @@ module Web.HttpApiData (
148 -- * Classes
159 ToHttpApiData (..),
1610 FromHttpApiData (..),
17−
18− -- * Utility functions
19− parseMaybeTextData,
20− showTextData,
21− readMaybeTextData,
22− readEitherTextData,
2311) where
2412
25−import Control.Applicative
26−import Control.Arrow ((&&&))
27−
28−import Data.Monoid
29−import Data.ByteString (ByteString)
30−
31−import Data.Int
32−import Data.Word
33−
34−import Data.Text (Text)
35−import Data.Text.Encoding (encodeUtf8, decodeUtf8)
36−import Data.Text.Read (signed, decimal, rational, Reader)
37−import qualified Data.Text as T
38−import qualified Data.Text.Lazy as L
39−
40−import Data.Time (Day)
41−import Data.Version
42−
43−#if MIN_VERSION_base(4,8,0)
44−import Data.Void
45−#endif
46−
47−import Text.Read (readMaybe)
48−import Text.ParserCombinators.ReadP (readP_to_S)
13+import Web.HttpApiData.Internal
4914
5015-- $setup
5116--
5217-- >>> :set -XOverloadedStrings
5318-- >>> import Control.Applicative
5419-- >>> import Data.Time
55−
56−-- | Convert value to HTTP API data.
57−class ToHttpApiData a where
58− {-# MINIMAL toUrlPiece | toQueryParam #-}
59− -- | Convert to URL path piece.
60− toUrlPiece :: a -> Text
61− toUrlPiece = toQueryParam
62−
63− -- | Convert to HTTP header value.
64− toHeader :: a -> ByteString
65− toHeader = encodeUtf8 . toUrlPiece
66−
67− -- | Convert to query param value.
68− toQueryParam :: a -> Text
69− toQueryParam = toUrlPiece
70−
71−-- | Convert value from HTTP API data.
72−class FromHttpApiData a where
73− {-# MINIMAL parseUrlPiece | parseQueryParam #-}
74− -- | Parse URL path piece.
75− parseUrlPiece :: Text -> Either Text a
76− parseUrlPiece = parseQueryParam
77−
78− -- | Parse HTTP header value.
79− parseHeader :: ByteString -> Either Text a
80− parseHeader = parseUrlPiece . decodeUtf8
81−
82− -- | Parse query param value.
83− parseQueryParam :: Text -> Either Text a
84− parseQueryParam = parseUrlPiece
85−
86−-- | Default parsing error.
87−defaultParseError :: Text -> Either Text a
88−defaultParseError input = Left ("could not convert: `" <> input <> "'")
89−
90−-- | Convert @'Maybe'@ parser into @'Either' 'Text'@ parser with default error message.
91−parseMaybeTextData :: (Text -> Maybe a) -> (Text -> Either Text a)
92−parseMaybeTextData parse input =
93− case parse input of
94− Nothing -> defaultParseError input
95− Just val -> Right val
96−
97−-- | Convert to URL piece using @'Show'@ instance.
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]
124−
125−-- | Parse URL piece using @'Read'@ instance.
126−readMaybeTextData :: Read a => Text -> Maybe a
127−readMaybeTextData = readMaybe . T.unpack
128−
129−-- | Parse URL piece using @'Read'@ instance.
130−readEitherTextData :: Read a => Text -> Either Text a
131−readEitherTextData = parseMaybeTextData readMaybeTextData
132−
133−-- | Run @'Reader'@ as HTTP API data parser.
134−runReader :: Reader a -> Text -> Either Text a
135−runReader reader input =
136− case reader input of
137− Left err -> Left (T.pack err)
138− Right (x, rest)
139− | T.null rest -> Right x
140− | otherwise -> defaultParseError input
141−
142−-- | Run @'Reader'@ to parse bounded integral value with bounds checking.
143−parseBounded :: forall a. (Bounded a, Integral a) => Reader Integer -> Text -> Either Text a
144−parseBounded reader input = do
145− n <- runReader reader input
146− if (n > h || n < l)
147− then Left ("out of bounds: `" <> input <> "' (should be between " <> T.pack (show l) <> " and " <> T.pack (show h) <> ")")
148− else Right (fromInteger n)
149− where
150− l = toInteger (minBound :: a)
151− h = toInteger (maxBound :: a)
152−
153−-- |
154−-- >>> toUrlPiece ()
155−-- "_"
156−instance ToHttpApiData () where
157− toUrlPiece () = "_"
158−
159−instance ToHttpApiData Char where toUrlPiece = T.singleton
160−
161−-- |
162−-- >>> toUrlPiece (Version [1, 2, 3] [])
163−-- "1.2.3"
164−instance ToHttpApiData Version where
165− toUrlPiece = T.pack . showVersion
166−
167−#if MIN_VERSION_base(4,8,0)
168−instance ToHttpApiData Void where
169− toUrlPiece = absurd
170−#endif
171−
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
187−instance ToHttpApiData String where toUrlPiece = T.pack
188−instance ToHttpApiData Text where toUrlPiece = id
189−instance ToHttpApiData L.Text where toUrlPiece = L.toStrict
190−instance ToHttpApiData Day where toUrlPiece = showTextData
191−
192−instance ToHttpApiData All where toUrlPiece = toUrlPiece . getAll
193−instance ToHttpApiData Any where toUrlPiece = toUrlPiece . getAny
194−
195−instance ToHttpApiData a => ToHttpApiData (Dual a) where toUrlPiece = toUrlPiece . getDual
196−instance ToHttpApiData a => ToHttpApiData (Sum a) where toUrlPiece = toUrlPiece . getSum
197−instance ToHttpApiData a => ToHttpApiData (Product a) where toUrlPiece = toUrlPiece . getProduct
198−instance ToHttpApiData a => ToHttpApiData (First a) where toUrlPiece = toUrlPiece . getFirst
199−instance ToHttpApiData a => ToHttpApiData (Last a) where toUrlPiece = toUrlPiece . getLast
200−
201−-- |
202−-- >>> toUrlPiece (Just "Hello")
203−-- "just Hello"
204−instance ToHttpApiData a => ToHttpApiData (Maybe a) where
205− toUrlPiece (Just x) = "just " <> toUrlPiece x
206− toUrlPiece Nothing = "nothing"
207−
208−-- |
209−-- >>> toUrlPiece (Left "err" :: Either String Int)
210−-- "left err"
211−-- >>> toUrlPiece (Right 3 :: Either String Int)
212−-- "right 3"
213−instance (ToHttpApiData a, ToHttpApiData b) => ToHttpApiData (Either a b) where
214− toUrlPiece (Left x) = "left " <> toUrlPiece x
215− toUrlPiece (Right x) = "right " <> toUrlPiece x
216−
217−-- |
218−-- >>> parseUrlPiece "_" :: Either Text ()
219−-- Right ()
220−instance FromHttpApiData () where
221− parseUrlPiece "_" = return ()
222− parseUrlPiece s = defaultParseError s
223−
224−instance FromHttpApiData Char where
225− parseUrlPiece s =
226− case T.uncons s of
227− Just (c, s') | T.null s' -> return c
228− _ -> defaultParseError s
229−
230−-- |
231−-- >>> showVersion <$> parseUrlPiece "1.2.3"
232−-- Right "1.2.3"
233−instance FromHttpApiData Version where
234− parseUrlPiece s =
235− case reverse (readP_to_S parseVersion (T.unpack s)) of
236− ((x, ""):_) -> return x
237− _ -> defaultParseError s
238−
239−#if MIN_VERSION_base(4,8,0)
240−instance FromHttpApiData Void where
241− parseUrlPiece _ = Left "Void cannot be parsed!"
242−#endif
243−
244−instance FromHttpApiData Bool where parseUrlPiece = parseBoundedCaseInsensitiveTextData
245−instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedCaseInsensitiveTextData
246−instance FromHttpApiData Double where parseUrlPiece = runReader rational
247−instance FromHttpApiData Float where parseUrlPiece = runReader rational
248−instance FromHttpApiData Int where parseUrlPiece = parseBounded (signed decimal)
249−instance FromHttpApiData Int8 where parseUrlPiece = parseBounded (signed decimal)
250−instance FromHttpApiData Int16 where parseUrlPiece = parseBounded (signed decimal)
251−instance FromHttpApiData Int32 where parseUrlPiece = parseBounded (signed decimal)
252−instance FromHttpApiData Int64 where parseUrlPiece = parseBounded (signed decimal)
253−instance FromHttpApiData Integer where parseUrlPiece = runReader (signed decimal)
254−instance FromHttpApiData Word where parseUrlPiece = parseBounded decimal
255−instance FromHttpApiData Word8 where parseUrlPiece = parseBounded decimal
256−instance FromHttpApiData Word16 where parseUrlPiece = parseBounded decimal
257−instance FromHttpApiData Word32 where parseUrlPiece = parseBounded decimal
258−instance FromHttpApiData Word64 where parseUrlPiece = parseBounded decimal
259−instance FromHttpApiData String where parseUrlPiece = Right . T.unpack
260−instance FromHttpApiData Text where parseUrlPiece = Right
261−instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
262−instance FromHttpApiData Day where parseUrlPiece = readEitherTextData
263−
264−instance FromHttpApiData All where parseUrlPiece = fmap All . parseUrlPiece
265−instance FromHttpApiData Any where parseUrlPiece = fmap Any . parseUrlPiece
266−
267−instance FromHttpApiData a => FromHttpApiData (Dual a) where parseUrlPiece = fmap Dual . parseUrlPiece
268−instance FromHttpApiData a => FromHttpApiData (Sum a) where parseUrlPiece = fmap Sum . parseUrlPiece
269−instance FromHttpApiData a => FromHttpApiData (Product a) where parseUrlPiece = fmap Product . parseUrlPiece
270−instance FromHttpApiData a => FromHttpApiData (First a) where parseUrlPiece = fmap First . parseUrlPiece
271−instance FromHttpApiData a => FromHttpApiData (Last a) where parseUrlPiece = fmap Last . parseUrlPiece
272−
273−-- |
274−-- >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
275−-- Right (Just 123)
276−instance FromHttpApiData a => FromHttpApiData (Maybe a) where
277− parseUrlPiece s
278− | T.toLower (T.take 7 s) == "nothing" = return Nothing
279− | otherwise = Just <$> parseUrlPieceWithPrefix "Just " s
280−
281−-- |
282−-- >>> parseUrlPiece "Right 123" :: Either Text (Either String Int)
283−-- Right (Right 123)
284−instance (FromHttpApiData a, FromHttpApiData b) => FromHttpApiData (Either a b) where
285− parseUrlPiece s =
286− Right <$> parseUrlPieceWithPrefix "Right " s
287− <!> Left <$> parseUrlPieceWithPrefix "Left " s
288− where
289− infixl 3 <!>
290− Left _ <!> y = y
291− x <!> _ = x
20+-- >>> import Data.Int
21+-- >>> import Data.Text (Text)
22+-- >>> import Data.Time (Day)
23+-- >>> import Data.Version
29224
29325-- $examples
29426--
Web/HttpApiData/Internal.hsnew file+273
@@ -0,0 +1,273 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeSynonymInstances #-}
7+-- |
8+-- Convert Haskell values to and from HTTP API data
9+-- such as URL pieces, headers and query parameters.
10+module Web.HttpApiData.Internal where
11+
12+import Control.Applicative
13+import Control.Arrow ((&&&))
14+
15+import Data.Monoid
16+import Data.ByteString (ByteString)
17+
18+import Data.Int
19+import Data.Word
20+
21+import Data.Text (Text)
22+import Data.Text.Encoding (encodeUtf8, decodeUtf8)
23+import Data.Text.Read (signed, decimal, rational, Reader)
24+import qualified Data.Text as T
25+import qualified Data.Text.Lazy as L
26+
27+import Data.Time (Day)
28+import Data.Version
29+
30+#if MIN_VERSION_base(4,8,0)
31+import Data.Void
32+#endif
33+
34+import Text.Read (readMaybe)
35+import Text.ParserCombinators.ReadP (readP_to_S)
36+
37+-- | Convert value to HTTP API data.
38+class ToHttpApiData a where
39+ {-# MINIMAL toUrlPiece | toQueryParam #-}
40+ -- | Convert to URL path piece.
41+ toUrlPiece :: a -> Text
42+ toUrlPiece = toQueryParam
43+
44+ -- | Convert to HTTP header value.
45+ toHeader :: a -> ByteString
46+ toHeader = encodeUtf8 . toUrlPiece
47+
48+ -- | Convert to query param value.
49+ toQueryParam :: a -> Text
50+ toQueryParam = toUrlPiece
51+
52+-- | Convert value from HTTP API data.
53+class FromHttpApiData a where
54+ {-# MINIMAL parseUrlPiece | parseQueryParam #-}
55+ -- | Parse URL path piece.
56+ parseUrlPiece :: Text -> Either Text a
57+ parseUrlPiece = parseQueryParam
58+
59+ -- | Parse HTTP header value.
60+ parseHeader :: ByteString -> Either Text a
61+ parseHeader = parseUrlPiece . decodeUtf8
62+
63+ -- | Parse query param value.
64+ parseQueryParam :: Text -> Either Text a
65+ parseQueryParam = parseUrlPiece
66+
67+-- | Default parsing error.
68+defaultParseError :: Text -> Either Text a
69+defaultParseError input = Left ("could not convert: `" <> input <> "'")
70+
71+-- | Convert @'Maybe'@ parser into @'Either' 'Text'@ parser with default error message.
72+parseMaybeTextData :: (Text -> Maybe a) -> (Text -> Either Text a)
73+parseMaybeTextData parse input =
74+ case parse input of
75+ Nothing -> defaultParseError input
76+ Just val -> Right val
77+
78+-- | Convert to URL piece using @'Show'@ instance.
79+showTextData :: Show a => a -> Text
80+showTextData = T.toLower . T.pack . show
81+
82+-- | Parse given text case insensitive and return the rest of the input.
83+--
84+-- >>> parseUrlPieceWithPrefix "Just " "just 10" :: Either Text Int
85+-- Right 10
86+-- >>> parseUrlPieceWithPrefix "Left " "left" :: Either Text Bool
87+-- Left "could not convert: `left'"
88+parseUrlPieceWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a
89+parseUrlPieceWithPrefix pattern input
90+ | T.toLower pattern == T.toLower prefix = parseUrlPiece rest
91+ | otherwise = defaultParseError input
92+ where
93+ (prefix, rest) = T.splitAt (T.length pattern) input
94+
95+-- | Parse values case insensitively based on @'Show'@ instance.
96+--
97+-- >>> parseBoundedCaseInsensitiveTextData "true" :: Either Text Bool
98+-- Right True
99+-- >>> parseBoundedCaseInsensitiveTextData "FALSE" :: Either Text Bool
100+-- Right False
101+parseBoundedCaseInsensitiveTextData :: forall a. (Show a, Bounded a, Enum a) => Text -> Either Text a
102+parseBoundedCaseInsensitiveTextData = parseMaybeTextData (flip lookup values . T.toLower)
103+ where
104+ values = map (showTextData &&& id) [minBound..maxBound :: a]
105+
106+-- | Parse URL piece using @'Read'@ instance.
107+readMaybeTextData :: Read a => Text -> Maybe a
108+readMaybeTextData = readMaybe . T.unpack
109+
110+-- | Parse URL piece using @'Read'@ instance.
111+readEitherTextData :: Read a => Text -> Either Text a
112+readEitherTextData = parseMaybeTextData readMaybeTextData
113+
114+-- | Run @'Reader'@ as HTTP API data parser.
115+runReader :: Reader a -> Text -> Either Text a
116+runReader reader input =
117+ case reader input of
118+ Left err -> Left (T.pack err)
119+ Right (x, rest)
120+ | T.null rest -> Right x
121+ | otherwise -> defaultParseError input
122+
123+-- | Run @'Reader'@ to parse bounded integral value with bounds checking.
124+parseBounded :: forall a. (Bounded a, Integral a) => Reader Integer -> Text -> Either Text a
125+parseBounded reader input = do
126+ n <- runReader reader input
127+ if (n > h || n < l)
128+ then Left ("out of bounds: `" <> input <> "' (should be between " <> T.pack (show l) <> " and " <> T.pack (show h) <> ")")
129+ else Right (fromInteger n)
130+ where
131+ l = toInteger (minBound :: a)
132+ h = toInteger (maxBound :: a)
133+
134+-- |
135+-- >>> toUrlPiece ()
136+-- "_"
137+instance ToHttpApiData () where
138+ toUrlPiece () = "_"
139+
140+instance ToHttpApiData Char where toUrlPiece = T.singleton
141+
142+-- |
143+-- >>> toUrlPiece (Version [1, 2, 3] [])
144+-- "1.2.3"
145+instance ToHttpApiData Version where
146+ toUrlPiece = T.pack . showVersion
147+
148+#if MIN_VERSION_base(4,8,0)
149+instance ToHttpApiData Void where
150+ toUrlPiece = absurd
151+#endif
152+
153+instance ToHttpApiData Bool where toUrlPiece = showTextData
154+instance ToHttpApiData Ordering where toUrlPiece = showTextData
155+instance ToHttpApiData Double where toUrlPiece = showTextData
156+instance ToHttpApiData Float where toUrlPiece = showTextData
157+instance ToHttpApiData Int where toUrlPiece = showTextData
158+instance ToHttpApiData Int8 where toUrlPiece = showTextData
159+instance ToHttpApiData Int16 where toUrlPiece = showTextData
160+instance ToHttpApiData Int32 where toUrlPiece = showTextData
161+instance ToHttpApiData Int64 where toUrlPiece = showTextData
162+instance ToHttpApiData Integer where toUrlPiece = showTextData
163+instance ToHttpApiData Word where toUrlPiece = showTextData
164+instance ToHttpApiData Word8 where toUrlPiece = showTextData
165+instance ToHttpApiData Word16 where toUrlPiece = showTextData
166+instance ToHttpApiData Word32 where toUrlPiece = showTextData
167+instance ToHttpApiData Word64 where toUrlPiece = showTextData
168+instance ToHttpApiData String where toUrlPiece = T.pack
169+instance ToHttpApiData Text where toUrlPiece = id
170+instance ToHttpApiData L.Text where toUrlPiece = L.toStrict
171+instance ToHttpApiData Day where toUrlPiece = showTextData
172+
173+instance ToHttpApiData All where toUrlPiece = toUrlPiece . getAll
174+instance ToHttpApiData Any where toUrlPiece = toUrlPiece . getAny
175+
176+instance ToHttpApiData a => ToHttpApiData (Dual a) where toUrlPiece = toUrlPiece . getDual
177+instance ToHttpApiData a => ToHttpApiData (Sum a) where toUrlPiece = toUrlPiece . getSum
178+instance ToHttpApiData a => ToHttpApiData (Product a) where toUrlPiece = toUrlPiece . getProduct
179+instance ToHttpApiData a => ToHttpApiData (First a) where toUrlPiece = toUrlPiece . getFirst
180+instance ToHttpApiData a => ToHttpApiData (Last a) where toUrlPiece = toUrlPiece . getLast
181+
182+-- |
183+-- >>> toUrlPiece (Just "Hello")
184+-- "just Hello"
185+instance ToHttpApiData a => ToHttpApiData (Maybe a) where
186+ toUrlPiece (Just x) = "just " <> toUrlPiece x
187+ toUrlPiece Nothing = "nothing"
188+
189+-- |
190+-- >>> toUrlPiece (Left "err" :: Either String Int)
191+-- "left err"
192+-- >>> toUrlPiece (Right 3 :: Either String Int)
193+-- "right 3"
194+instance (ToHttpApiData a, ToHttpApiData b) => ToHttpApiData (Either a b) where
195+ toUrlPiece (Left x) = "left " <> toUrlPiece x
196+ toUrlPiece (Right x) = "right " <> toUrlPiece x
197+
198+-- |
199+-- >>> parseUrlPiece "_" :: Either Text ()
200+-- Right ()
201+instance FromHttpApiData () where
202+ parseUrlPiece "_" = return ()
203+ parseUrlPiece s = defaultParseError s
204+
205+instance FromHttpApiData Char where
206+ parseUrlPiece s =
207+ case T.uncons s of
208+ Just (c, s') | T.null s' -> return c
209+ _ -> defaultParseError s
210+
211+-- |
212+-- >>> showVersion <$> parseUrlPiece "1.2.3"
213+-- Right "1.2.3"
214+instance FromHttpApiData Version where
215+ parseUrlPiece s =
216+ case reverse (readP_to_S parseVersion (T.unpack s)) of
217+ ((x, ""):_) -> return x
218+ _ -> defaultParseError s
219+
220+#if MIN_VERSION_base(4,8,0)
221+instance FromHttpApiData Void where
222+ parseUrlPiece _ = Left "Void cannot be parsed!"
223+#endif
224+
225+instance FromHttpApiData Bool where parseUrlPiece = parseBoundedCaseInsensitiveTextData
226+instance FromHttpApiData Ordering where parseUrlPiece = parseBoundedCaseInsensitiveTextData
227+instance FromHttpApiData Double where parseUrlPiece = runReader rational
228+instance FromHttpApiData Float where parseUrlPiece = runReader rational
229+instance FromHttpApiData Int where parseUrlPiece = parseBounded (signed decimal)
230+instance FromHttpApiData Int8 where parseUrlPiece = parseBounded (signed decimal)
231+instance FromHttpApiData Int16 where parseUrlPiece = parseBounded (signed decimal)
232+instance FromHttpApiData Int32 where parseUrlPiece = parseBounded (signed decimal)
233+instance FromHttpApiData Int64 where parseUrlPiece = parseBounded (signed decimal)
234+instance FromHttpApiData Integer where parseUrlPiece = runReader (signed decimal)
235+instance FromHttpApiData Word where parseUrlPiece = parseBounded decimal
236+instance FromHttpApiData Word8 where parseUrlPiece = parseBounded decimal
237+instance FromHttpApiData Word16 where parseUrlPiece = parseBounded decimal
238+instance FromHttpApiData Word32 where parseUrlPiece = parseBounded decimal
239+instance FromHttpApiData Word64 where parseUrlPiece = parseBounded decimal
240+instance FromHttpApiData String where parseUrlPiece = Right . T.unpack
241+instance FromHttpApiData Text where parseUrlPiece = Right
242+instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
243+instance FromHttpApiData Day where parseUrlPiece = readEitherTextData
244+
245+instance FromHttpApiData All where parseUrlPiece = fmap All . parseUrlPiece
246+instance FromHttpApiData Any where parseUrlPiece = fmap Any . parseUrlPiece
247+
248+instance FromHttpApiData a => FromHttpApiData (Dual a) where parseUrlPiece = fmap Dual . parseUrlPiece
249+instance FromHttpApiData a => FromHttpApiData (Sum a) where parseUrlPiece = fmap Sum . parseUrlPiece
250+instance FromHttpApiData a => FromHttpApiData (Product a) where parseUrlPiece = fmap Product . parseUrlPiece
251+instance FromHttpApiData a => FromHttpApiData (First a) where parseUrlPiece = fmap First . parseUrlPiece
252+instance FromHttpApiData a => FromHttpApiData (Last a) where parseUrlPiece = fmap Last . parseUrlPiece
253+
254+-- |
255+-- >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
256+-- Right (Just 123)
257+instance FromHttpApiData a => FromHttpApiData (Maybe a) where
258+ parseUrlPiece s
259+ | T.toLower (T.take 7 s) == "nothing" = return Nothing
260+ | otherwise = Just <$> parseUrlPieceWithPrefix "Just " s
261+
262+-- |
263+-- >>> parseUrlPiece "Right 123" :: Either Text (Either String Int)
264+-- Right (Right 123)
265+instance (FromHttpApiData a, FromHttpApiData b) => FromHttpApiData (Either a b) where
266+ parseUrlPiece s =
267+ Right <$> parseUrlPieceWithPrefix "Right " s
268+ <!> Left <$> parseUrlPieceWithPrefix "Left " s
269+ where
270+ infixl 3 <!>
271+ Left _ <!> y = y
272+ x <!> _ = x
273+
Web/PathPieces.hs+1
@@ -31,6 +31,7 @@ import Data.Void (Void)
3131#endif
3232
3333import Web.HttpApiData
34+import Web.HttpApiData.Internal (parseMaybeTextData)
3435
3536-- | Convert Haskell values to and from route piece.
3637class PathPiece s where
http-api-data.cabal+1
@@ -24,6 +24,7 @@ library
2424 exposed-modules:
2525 Web.PathPieces
2626 Web.HttpApiData
27+ Web.HttpApiData.Internal
2728 ghc-options: -Wall
2829
2930test-suite test