{-# LANGUAGE FlexibleInstances #-}

module Network.HTTP.Kinder.URI (

  -- * Classes for encoding and decoding
    URIEncode (..)
  , URIDecode (..)

  -- * Extra serialization utilities
  , uriDecodeBS

) where

import qualified Data.ByteString            as S
import           Data.Int
import           Data.Text                  (Text)
import qualified Data.Text                  as Text
import qualified Data.Text.Encoding         as Enc
import           Data.Text.Read             (decimal)
import           Network.HTTP.Kinder.Common

-- Class
-- ----------------------------------------------------------------------------

-- | Determines a 'Text' serialization of a type with URI segment
-- representation.
class URIEncode a where
  uriEncode :: a -> Text

-- | Parses a 'Text' serialization of a for some type from a representation
-- as a URI segment.
class URIDecode a where
  uriDecode :: Text -> Either String a

-- | Since 'URIDecode' assumes a 'Text'-like representation but we may read
-- a 'S.ByteString' off the wire, this helper function first tries to
-- decode the 'S.ByteString' as UTF-8 'Text' then decodes according to
-- 'URIDecode'.
uriDecodeBS :: URIDecode a => S.ByteString -> Either String a
uriDecodeBS s = case Enc.decodeUtf8' s of
  Left _err -> Left "could not parse UTF8 string"
  Right a -> uriDecode a

-- Instances
-- ----------------------------------------------------------------------------

instance URIDecode (Raw Text) where
  uriDecode text = Right (Raw text)

-- | For URI segments this instance is the same as the one for @'Raw'
-- 'Text'@ since URI segments don't contain quotations.
instance URIDecode Text where
  uriDecode text = Right text

-- | Decoder for any type with a decimal representation. Requires a 'Show'
-- instance for the error message.
uriDecodeDecimal :: (Show a, Integral a) => Text -> Either String a
uriDecodeDecimal txt =
  case decimal txt of
    Left err -> Left err
    Right (a, t)
      | Text.null t -> Right a
      | otherwise -> Left ("incomplete parse: " ++ show (a, t))

instance URIDecode Int where uriDecode = uriDecodeDecimal
instance URIDecode Int8 where uriDecode = uriDecodeDecimal
instance URIDecode Int16 where uriDecode = uriDecodeDecimal
instance URIDecode Int32 where uriDecode = uriDecodeDecimal
instance URIDecode Int64 where uriDecode = uriDecodeDecimal
instance URIDecode Integer where uriDecode = uriDecodeDecimal