1{-# LANGUAGE DefaultSignatures #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE TypeSynonymInstances #-}
6-- Convert Haskell values to and from HTTP API data
7-- such as URL pieces, headers and query parameters.
8module Web.HttpApiData (
15 -- * Utility functions
16 parseMaybeHttpApiData,
22import Control.Arrow ((+++))
25import Data.ByteString (ByteString)
30import Data.Text (Text)
31import Data.Text.Encoding (encodeUtf8, decodeUtf8)
32import Data.Text.Read (signed, decimal, rational, Reader)
33import qualified Data.Text as Text
34import qualified Data.Text.Lazy as TL
38import Text.Read (readMaybe)
42-- >>> :set -XOverloadedStrings
43-- >>> import Data.Time
45-- | Convert value to HTTP API data.
46class ToHttpApiData a where
47 -- | Convert to URL path piece.
48 toUrlPiece :: a -> Text
49 toUrlPiece = toQueryParam
51 -- | Convert to HTTP header value.
52 toHeader :: a -> ByteString
53 toHeader = encodeUtf8 . toUrlPiece
55 -- | Convert to query param value.
56 toQueryParam :: a -> Text
57 toQueryParam = toUrlPiece
59-- | Convert value from HTTP API data.
60class FromHttpApiData a where
61 -- | Parse URL path piece.
62 parseUrlPiece :: Text -> Either Text a
63 parseUrlPiece = parseQueryParam
65 -- | Parse HTTP header value.
66 parseHeader :: ByteString -> Either Text a
67 parseHeader = parseUrlPiece . decodeUtf8
69 -- | Parse query param value.
70 parseQueryParam :: Text -> Either Text a
71 parseQueryParam = parseUrlPiece
73-- | Convert @'Maybe'@ parser into @'Either' 'Text'@ parser with default error message.
74parseMaybeHttpApiData :: (Text -> Maybe a) -> (Text -> Either Text a)
75parseMaybeHttpApiData parse input =
77 Nothing -> Left ("could not convert: `" <> input <> "'")
80-- | Convert to URL piece using @'Show'@ instance.
81showUrlPiece :: Show a => a -> Text
82showUrlPiece = Text.pack . show
84-- | Parse URL piece using @'Read'@ instance.
85readMaybeUrlPiece :: Read a => Text -> Maybe a
86readMaybeUrlPiece = readMaybe . Text.unpack
88-- | Parse URL piece using @'Read'@ instance.
89readEitherUrlPiece :: Read a => Text -> Either Text a
90readEitherUrlPiece = parseMaybeHttpApiData readMaybeUrlPiece
92-- | Run @'Reader'@ as HTTP API data parser.
93runReader :: Reader a -> Text -> Either Text a
94runReader reader = (Text.pack +++ fst) . reader
96instance ToHttpApiData Bool where toUrlPiece = showUrlPiece
97instance ToHttpApiData Double where toUrlPiece = showUrlPiece
98instance ToHttpApiData Float where toUrlPiece = showUrlPiece
99instance ToHttpApiData Int where toUrlPiece = showUrlPiece
100instance ToHttpApiData Int8 where toUrlPiece = showUrlPiece
101instance ToHttpApiData Int16 where toUrlPiece = showUrlPiece
102instance ToHttpApiData Int32 where toUrlPiece = showUrlPiece
103instance ToHttpApiData Int64 where toUrlPiece = showUrlPiece
104instance ToHttpApiData Integer where toUrlPiece = showUrlPiece
105instance ToHttpApiData Word where toUrlPiece = showUrlPiece
106instance ToHttpApiData Word8 where toUrlPiece = showUrlPiece
107instance ToHttpApiData Word16 where toUrlPiece = showUrlPiece
108instance ToHttpApiData Word32 where toUrlPiece = showUrlPiece
109instance ToHttpApiData Word64 where toUrlPiece = showUrlPiece
110instance ToHttpApiData String where toUrlPiece = Text.pack
111instance ToHttpApiData Text where toUrlPiece = id
112instance ToHttpApiData TL.Text where toUrlPiece = TL.toStrict
113instance ToHttpApiData Day where toUrlPiece = showUrlPiece
115instance FromHttpApiData Bool where parseUrlPiece = readEitherUrlPiece
116instance FromHttpApiData Double where parseUrlPiece = runReader rational
117instance FromHttpApiData Float where parseUrlPiece = runReader rational
118instance FromHttpApiData Int where parseUrlPiece = runReader (signed decimal)
119instance FromHttpApiData Int8 where parseUrlPiece = runReader (signed decimal)
120instance FromHttpApiData Int16 where parseUrlPiece = runReader (signed decimal)
121instance FromHttpApiData Int32 where parseUrlPiece = runReader (signed decimal)
122instance FromHttpApiData Int64 where parseUrlPiece = runReader (signed decimal)
123instance FromHttpApiData Integer where parseUrlPiece = runReader (signed decimal)
124instance FromHttpApiData Word where parseUrlPiece = runReader decimal
125instance FromHttpApiData Word8 where parseUrlPiece = runReader decimal
126instance FromHttpApiData Word16 where parseUrlPiece = runReader decimal
127instance FromHttpApiData Word32 where parseUrlPiece = runReader decimal
128instance FromHttpApiData Word64 where parseUrlPiece = runReader decimal
129instance FromHttpApiData String where parseUrlPiece = Right . Text.unpack
130instance FromHttpApiData Text where parseUrlPiece = Right
131instance FromHttpApiData TL.Text where parseUrlPiece = Right . TL.fromStrict
132instance FromHttpApiData Day where parseUrlPiece = readEitherUrlPiece
138-- >>> toUrlPiece True
140-- >>> parseUrlPiece "False" :: Either Text Bool
142-- >>> parseUrlPiece "something else" :: Either Text Bool
143-- Left "could not convert: `something else'"
147-- >>> toUrlPiece 45.2
149-- >>> parseUrlPiece "452" :: Either Text Int
154-- >>> toHeader "hello"
156-- >>> parseHeader "world" :: Either Text String
161-- >>> toQueryParam (fromGregorian 2015 10 03)
163-- >>> toGregorian <$> parseQueryParam "2016-12-01"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list