Web.HttpApiData

http-api-data-0.2.2: Converting to/from HTTP API data like URL pieces, headers and query parameters.

Safe Haskell | Safe
Language | Haskell98

Web.HttpApiData

Contents

- Examples
- Classes
- ``Maybe`` parsers
- Prefix parsers
- Multiple URL pieces
- Multiple query params
- Other helpers

Description

Convert Haskell values to and from HTTP API data such as URL pieces, headers and query parameters.

Synopsis

- class ToHttpApiData a where
  - toUrlPiece :: a -> Text
  - toHeader :: a -> ByteString
  - toQueryParam :: a -> Text
- class FromHttpApiData a where
  - parseUrlPiece :: Text -> Either Text a
  - parseHeader :: ByteString -> Either Text a
  - parseQueryParam :: Text -> Either Text a
- parseUrlPieceMaybe :: FromHttpApiData a => Text -> Maybe a
- parseHeaderMaybe :: FromHttpApiData a => ByteString -> Maybe a
- parseQueryParamMaybe :: FromHttpApiData a => Text -> Maybe a
- parseUrlPieceWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a
- parseHeaderWithPrefix :: FromHttpApiData a => ByteString -> ByteString -> Either Text a
- parseQueryParamWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a
- toUrlPieces :: (Functor t, ToHttpApiData a) => t a -> t Text
- parseUrlPieces :: (Traversable t, FromHttpApiData a) => t Text -> Either Text (t a)
- toQueryParams :: (Functor t, ToHttpApiData a) => t a -> t Text
- parseQueryParams :: (Traversable t, FromHttpApiData a) => t Text -> Either Text (t a)
- showTextData :: Show a => a -> Text
- readTextData :: Read a => Text -> Either Text a
- parseBoundedTextData :: (Show a, Bounded a, Enum a) => Text -> Either Text a

# Examples

Booleans:

    >>> toUrlPiece True
    "true"
    >>> parseUrlPiece "false" :: Either Text Bool
    Right False
    >>> parseUrlPieces ["true", "false", "undefined"] :: Either Text [Bool]
    Left "could not parse: `undefined'"

Numbers:

    >>> toQueryParam 45.2
    "45.2"
    >>> parseQueryParam "452" :: Either Text Int
    Right 452
    >>> toQueryParams [1..5]
    ["1","2","3","4","5"]
    >>> parseQueryParams ["127", "255"] :: Either Text [Int8]
    Left "out of bounds: `255' (should be between -128 and 127)"

Strings:

    >>> toHeader "hello"
    "hello"
    >>> parseHeader "world" :: Either Text String
    Right "world"

Calendar day:

    >>> toQueryParam (fromGregorian 2015 10 03)
    "2015-10-03"
    >>> toGregorian <$> parseQueryParam "2016-12-01"
    Right (2016,12,1)

# Classes

class ToHttpApiData a where

Convert value to HTTP API data.

Minimal complete definition

toUrlPiece | toQueryParam

Methods

toUrlPiece :: a -> Text

Convert to URL path piece.

toHeader :: a -> ByteString

Convert to HTTP header value.

toQueryParam :: a -> Text

Convert to query param value.

Instances

ToHttpApiData Bool |
ToHttpApiData Char |
ToHttpApiData Double |
ToHttpApiData Float |
ToHttpApiData Int |
ToHttpApiData Int8 |
ToHttpApiData Int16 |
ToHttpApiData Int32 |
ToHttpApiData Int64 |
ToHttpApiData Integer |
ToHttpApiData Ordering |
ToHttpApiData Word |
ToHttpApiData Word8 |
ToHttpApiData Word16 |
ToHttpApiData Word32 |
ToHttpApiData Word64 |
ToHttpApiData String |
ToHttpApiData () |
    >>> toUrlPiece ()
    "_"
ToHttpApiData Void |
ToHttpApiData Version |
    >>> toUrlPiece (Version [1, 2, 3] [])
    "1.2.3"
ToHttpApiData All |
ToHttpApiData Any |
ToHttpApiData Text |
ToHttpApiData Text |
ToHttpApiData LocalTime |
    >>> toUrlPiece $ LocalTime (fromGregorian 2015 10 03) (TimeOfDay 14 55 01)
    "2015-10-03T14:55:01"
ToHttpApiData ZonedTime |
    >>> toUrlPiece $ ZonedTime (LocalTime (fromGregorian 2015 10 03) (TimeOfDay 14 55 01)) utc
    "2015-10-03T14:55:01+0000"
ToHttpApiData UTCTime |
    >>> toUrlPiece $ UTCTime (fromGregorian 2015 10 03) 864
    "2015-10-03T00:14:24Z"
ToHttpApiData NominalDiffTime |
ToHttpApiData Day |
    >>> toUrlPiece (fromGregorian 2015 10 03)
    "2015-10-03"
ToHttpApiData a => ToHttpApiData (Dual a) |
ToHttpApiData a => ToHttpApiData (Sum a) |
ToHttpApiData a => ToHttpApiData (Product a) |
ToHttpApiData a => ToHttpApiData (First a) |
ToHttpApiData a => ToHttpApiData (Last a) |
ToHttpApiData a => ToHttpApiData (Maybe a) |
    >>> toUrlPiece (Just "Hello")
    "just Hello"
(ToHttpApiData a, ToHttpApiData b) => ToHttpApiData (Either a b) |
    >>> toUrlPiece (Left "err" :: Either String Int)
    "left err"
    >>> toUrlPiece (Right 3 :: Either String Int)
    "right 3"

class FromHttpApiData a where

Parse value from HTTP API data.

Minimal complete definition

parseUrlPiece | parseQueryParam

Methods

parseUrlPiece :: Text -> Either Text a

Parse URL path piece.

parseHeader :: ByteString -> Either Text a

Parse HTTP header value.

parseQueryParam :: Text -> Either Text a

Parse query param value.

Instances

FromHttpApiData Bool |
FromHttpApiData Char |
FromHttpApiData Double |
FromHttpApiData Float |
FromHttpApiData Int |
FromHttpApiData Int8 |
FromHttpApiData Int16 |
FromHttpApiData Int32 |
FromHttpApiData Int64 |
FromHttpApiData Integer |
FromHttpApiData Ordering |
FromHttpApiData Word |
FromHttpApiData Word8 |
FromHttpApiData Word16 |
FromHttpApiData Word32 |
FromHttpApiData Word64 |
FromHttpApiData String |
FromHttpApiData () |
    >>> parseUrlPiece "_" :: Either Text ()
    Right ()
FromHttpApiData Void | Parsing a ``Void`` value is always an error, considering ``Void`` as a data type with no constructors.
FromHttpApiData Version |
    >>> showVersion <$> parseUrlPiece "1.2.3"
    Right "1.2.3"
FromHttpApiData All |
FromHttpApiData Any |
FromHttpApiData Text |
FromHttpApiData Text |
FromHttpApiData LocalTime |
    >>> parseUrlPiece "2015-10-03T14:55:01" :: Either Text LocalTime
    Right 2015-10-03 14:55:01
FromHttpApiData ZonedTime |
    >>> parseUrlPiece "2015-10-03T14:55:01+0000" :: Either Text ZonedTime
    Right 2015-10-03 14:55:01 +0000
FromHttpApiData UTCTime |
    >>> parseUrlPiece "2015-10-03T00:14:24Z" :: Either Text UTCTime
    Right 2015-10-03 00:14:24 UTC
FromHttpApiData NominalDiffTime |
FromHttpApiData Day |
    >>> toGregorian <$> parseUrlPiece "2016-12-01"
    Right (2016,12,1)
FromHttpApiData a => FromHttpApiData (Dual a) |
FromHttpApiData a => FromHttpApiData (Sum a) |
FromHttpApiData a => FromHttpApiData (Product a) |
FromHttpApiData a => FromHttpApiData (First a) |
FromHttpApiData a => FromHttpApiData (Last a) |
FromHttpApiData a => FromHttpApiData (Maybe a) |
    >>> parseUrlPiece "Just 123" :: Either Text (Maybe Int)
    Right (Just 123)
(FromHttpApiData a, FromHttpApiData b) => FromHttpApiData (Either a b) |
    >>> parseUrlPiece "Right 123" :: Either Text (Either String Int)
    Right (Right 123)

# ``Maybe`` parsers

parseUrlPieceMaybe :: FromHttpApiData a => Text -> Maybe a

Parse URL path piece in a ``Maybe``.

    >>> parseUrlPieceMaybe "12" :: Maybe Int
    Just 12

parseHeaderMaybe :: FromHttpApiData a => ByteString -> Maybe a

Parse HTTP header value in a ``Maybe``.

    >>> parseHeaderMaybe "hello" :: Maybe Text
    Just "hello"

parseQueryParamMaybe :: FromHttpApiData a => Text -> Maybe a

Parse query param value in a ``Maybe``.

    >>> parseQueryParamMaybe "true" :: Maybe Bool
    Just True

# Prefix parsers

parseUrlPieceWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a

Case insensitive.

Parse given text case insensitive and then parse the rest of the input using ``parseUrlPiece``.

    >>> parseUrlPieceWithPrefix "Just " "just 10" :: Either Text Int
    Right 10
    >>> parseUrlPieceWithPrefix "Left " "left" :: Either Text Bool
    Left "could not parse: `left'"

This can be used to implement ``FromHttpApiData`` for single field constructors:

    >>> data Foo = Foo Int deriving (Show)
    >>> instance FromHttpApiData Foo where parseUrlPiece s = Foo <$> parseUrlPieceWithPrefix "Foo " s
    >>> parseUrlPiece "foo 1" :: Either Text Foo
    Right (Foo 1)

parseHeaderWithPrefix :: FromHttpApiData a => ByteString -> ByteString -> Either Text a

Parse given bytestring then parse the rest of the input using ``parseHeader``.

    data BasicAuthToken = BasicAuthToken Text deriving (Show)

    instance FromHttpApiData BasicAuthToken where
      parseHeader h     = BasicAuthToken <$> parseHeaderWithPrefix "Basic " h
      parseQueryParam p = BasicAuthToken <$> parseQueryParam p

    >>> parseHeader "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" :: Either Text BasicAuthToken
    Right (BasicAuthToken "QWxhZGRpbjpvcGVuIHNlc2FtZQ==")

parseQueryParamWithPrefix :: FromHttpApiData a => Text -> Text -> Either Text a

Case insensitive.

Parse given text case insensitive and then parse the rest of the input using ``parseQueryParam``.

    >>> parseQueryParamWithPrefix "z" "z10" :: Either Text Int
    Right 10

# Multiple URL pieces

toUrlPieces :: (Functor t, ToHttpApiData a) => t a -> t Text

Convert multiple values to a list of URL pieces.

    >>> toUrlPieces [1, 2, 3]
    ["1","2","3"]

parseUrlPieces :: (Traversable t, FromHttpApiData a) => t Text -> Either Text (t a)

Parse multiple URL pieces.

    >>> parseUrlPieces ["true", "false"] :: Either Text [Bool]
    Right [True,False]
    >>> parseUrlPieces ["123", "hello", "world"] :: Either Text [Int]
    Left "could not parse: `hello' (input does not start with a digit)"

# Multiple query params

toQueryParams :: (Functor t, ToHttpApiData a) => t a -> t Text

Convert multiple values to a list of query parameter values.

    >>> toQueryParams [fromGregorian 2015 10 03, fromGregorian 2015 12 01]
    ["2015-10-03","2015-12-01"]

parseQueryParams :: (Traversable t, FromHttpApiData a) => t Text -> Either Text (t a)

Parse multiple query parameters.

    >>> parseQueryParams ["1", "2", "3"] :: Either Text [Int]
    Right [1,2,3]
    >>> parseQueryParams ["64", "128", "256"] :: Either Text [Word8]
    Left "out of bounds: `256' (should be between 0 and 255)"

# Other helpers

showTextData :: Show a => a -> Text

Lower case.

Convert to URL piece using ``Show`` instance. The result is always lower cased.

    >>> showTextData True
    "true"

This can be used as a default implementation for enumeration types:

    >>> data MyData = Foo | Bar | Baz deriving (Show)
    >>> instance ToHttpApiData MyData where toUrlPiece = showTextData
    >>> toUrlPiece Foo
    "foo"

readTextData :: Read a => Text -> Either Text a

Parse URL piece using ``Read`` instance.

Use for types which do not involve letters:

    >>> readTextData "1991-06-02" :: Either Text Day
    Right 1991-06-02

This parser is case sensitive and will not match ``showTextData`` in presense of letters:

    >>> readTextData (showTextData True) :: Either Text Bool
    Left "could not parse: `true'"

See ``parseBoundedTextData``.

parseBoundedTextData :: (Show a, Bounded a, Enum a) => Text -> Either Text a

Case insensitive.

Parse values case insensitively based on ``Show`` instance.

    >>> parseBoundedTextData "true" :: Either Text Bool
    Right True
    >>> parseBoundedTextData "FALSE" :: Either Text Bool
    Right False

This can be used as a default implementation for enumeration types:

    >>> data MyData = Foo | Bar | Baz deriving (Show, Bounded, Enum)
    >>> instance FromHttpApiData MyData where parseUrlPiece = parseBoundedTextData
    >>> parseUrlPiece "foo" :: Either Text MyData
    Right Foo