-- |
-- Module      : Amazonka.Data.Text
-- Copyright   : (c) 2013-2021 Brendan Hay
-- License     : Mozilla Public License, v. 2.0.
-- Maintainer  : Brendan Hay <brendan.g.hay+amazonka@gmail.com>
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
module Amazonka.Data.Text
  ( Text,

    -- * Deserialisation
    FromText (..),

    -- * Serialisation
    ToText (..),
    toTextCI,
    showText,
  )
where

import qualified Amazonka.Bytes as Bytes
import qualified Amazonka.Crypto as Crypto
import Amazonka.Prelude
import qualified Data.Attoparsec.Text as A
import qualified Data.ByteString.Char8 as BS8
import qualified Data.CaseInsensitive as CI
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import qualified Data.Text.Lazy as LText
import qualified Data.Text.Lazy.Builder as Build
import qualified Data.Text.Lazy.Builder.Int as Build
import qualified Data.Text.Lazy.Builder.Scientific as Build
import qualified Network.HTTP.Types as HTTP
import qualified Numeric

class FromText a where
  fromText :: Text -> Either String a

instance FromText Text where
  fromText :: Text -> Either String Text
fromText = Text -> Either String Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure

instance FromText String where
  fromText :: Text -> Either String String
fromText = String -> Either String String
forall (f :: * -> *) a. Applicative f => a -> f a
pure (String -> Either String String)
-> (Text -> String) -> Text -> Either String String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack

instance FromText ByteString where
  fromText :: Text -> Either String ByteString
fromText = ByteString -> Either String ByteString
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Either String ByteString)
-> (Text -> ByteString) -> Text -> Either String ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
Text.encodeUtf8

instance (CI.FoldCase a, FromText a) => FromText (CI a) where
  fromText :: Text -> Either String (CI a)
fromText = (a -> CI a) -> Either String a -> Either String (CI a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> CI a
forall s. FoldCase s => s -> CI s
CI.mk (Either String a -> Either String (CI a))
-> (Text -> Either String a) -> Text -> Either String (CI a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String a
forall a. FromText a => Text -> Either String a
fromText

instance FromText Char where
  fromText :: Text -> Either String Char
fromText = Parser Char -> Text -> Either String Char
forall a. Parser a -> Text -> Either String a
A.parseOnly (Parser Char
A.anyChar Parser Char -> Parser Text () -> Parser Char
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Text ()
forall t. Chunk t => Parser t ()
A.endOfInput)

instance FromText Int where
  fromText :: Text -> Either String Int
fromText = Parser Int -> Text -> Either String Int
forall a. Parser a -> Text -> Either String a
A.parseOnly (Parser Int -> Parser Int
forall a. Num a => Parser a -> Parser a
A.signed Parser Int
forall a. Integral a => Parser a
A.decimal Parser Int -> Parser Text () -> Parser Int
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Text ()
forall t. Chunk t => Parser t ()
A.endOfInput)

instance FromText Int64 where
  fromText :: Text -> Either String Int64
fromText = Parser Int64 -> Text -> Either String Int64
forall a. Parser a -> Text -> Either String a
A.parseOnly (Parser Int64 -> Parser Int64
forall a. Num a => Parser a -> Parser a
A.signed Parser Int64
forall a. Integral a => Parser a
A.decimal Parser Int64 -> Parser Text () -> Parser Int64
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Text ()
forall t. Chunk t => Parser t ()
A.endOfInput)

instance FromText Integer where
  fromText :: Text -> Either String Integer
fromText = Parser Integer -> Text -> Either String Integer
forall a. Parser a -> Text -> Either String a
A.parseOnly (Parser Integer -> Parser Integer
forall a. Num a => Parser a -> Parser a
A.signed Parser Integer
forall a. Integral a => Parser a
A.decimal Parser Integer -> Parser Text () -> Parser Integer
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Text ()
forall t. Chunk t => Parser t ()
A.endOfInput)

instance FromText Scientific where
  fromText :: Text -> Either String Scientific
fromText = Parser Scientific -> Text -> Either String Scientific
forall a. Parser a -> Text -> Either String a
A.parseOnly (Parser Scientific -> Parser Scientific
forall a. Num a => Parser a -> Parser a
A.signed Parser Scientific
A.scientific Parser Scientific -> Parser Text () -> Parser Scientific
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Text ()
forall t. Chunk t => Parser t ()
A.endOfInput)

instance FromText Natural where
  fromText :: Text -> Either String Natural
fromText = Parser Natural -> Text -> Either String Natural
forall a. Parser a -> Text -> Either String a
A.parseOnly (Parser Natural
forall a. Integral a => Parser a
A.decimal Parser Natural -> Parser Text () -> Parser Natural
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Text ()
forall t. Chunk t => Parser t ()
A.endOfInput)

instance FromText Double where
  fromText :: Text -> Either String Double
fromText = Parser Double -> Text -> Either String Double
forall a. Parser a -> Text -> Either String a
A.parseOnly (Parser Double -> Parser Double
forall a. Num a => Parser a -> Parser a
A.signed Parser Double
forall a. Fractional a => Parser a
A.rational Parser Double -> Parser Text () -> Parser Double
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Text ()
forall t. Chunk t => Parser t ()
A.endOfInput)

instance FromText Bool where
  fromText :: Text -> Either String Bool
fromText Text
text =
    case Text -> CI Text
forall s. FoldCase s => s -> CI s
CI.mk Text
text of
      CI Text
"true" -> Bool -> Either String Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True
      CI Text
"false" -> Bool -> Either String Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
False
      CI Text
other -> String -> Either String Bool
forall a b. a -> Either a b
Left (String
"Failure parsing Bool from " String -> String -> String
forall a. [a] -> [a] -> [a]
++ CI Text -> String
forall a. Show a => a -> String
show CI Text
other String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".")

instance FromText HTTP.StdMethod where
  fromText :: Text -> Either String StdMethod
fromText Text
text =
    case ByteString -> Either ByteString StdMethod
HTTP.parseMethod (Text -> ByteString
Text.encodeUtf8 Text
text) of
      Left ByteString
err -> String -> Either String StdMethod
forall a b. a -> Either a b
Left (ByteString -> String
BS8.unpack ByteString
err)
      Right StdMethod
ok -> StdMethod -> Either String StdMethod
forall (f :: * -> *) a. Applicative f => a -> f a
pure StdMethod
ok

showText :: ToText a => a -> String
showText :: a -> String
showText = Text -> String
Text.unpack (Text -> String) -> (a -> Text) -> a -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Text
forall a. ToText a => a -> Text
toText

class ToText a where
  toText :: a -> Text

instance ToText a => ToText (CI a) where
  toText :: CI a -> Text
toText = a -> Text
forall a. ToText a => a -> Text
toText (a -> Text) -> (CI a -> a) -> CI a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CI a -> a
forall s. CI s -> s
CI.original

instance ToText Text where
  toText :: Text -> Text
toText = Text -> Text
forall a. a -> a
id

instance ToText ByteString where
  toText :: ByteString -> Text
toText = ByteString -> Text
Text.decodeUtf8

instance ToText Char where
  toText :: Char -> Text
toText = Char -> Text
Text.singleton

instance ToText String where
  toText :: String -> Text
toText = String -> Text
Text.pack

instance ToText Int where
  toText :: Int -> Text
toText = TextBuilder -> Text
shortText (TextBuilder -> Text) -> (Int -> TextBuilder) -> Int -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
Build.decimal

instance ToText Int64 where
  toText :: Int64 -> Text
toText = TextBuilder -> Text
shortText (TextBuilder -> Text) -> (Int64 -> TextBuilder) -> Int64 -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> TextBuilder
forall a. Integral a => a -> TextBuilder
Build.decimal

instance ToText Integer where
  toText :: Integer -> Text
toText = TextBuilder -> Text
shortText (TextBuilder -> Text)
-> (Integer -> TextBuilder) -> Integer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> TextBuilder
forall a. Integral a => a -> TextBuilder
Build.decimal

instance ToText Natural where
  toText :: Natural -> Text
toText = TextBuilder -> Text
shortText (TextBuilder -> Text)
-> (Natural -> TextBuilder) -> Natural -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Natural -> TextBuilder
forall a. Integral a => a -> TextBuilder
Build.decimal

instance ToText Scientific where
  toText :: Scientific -> Text
toText = TextBuilder -> Text
shortText (TextBuilder -> Text)
-> (Scientific -> TextBuilder) -> Scientific -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Scientific -> TextBuilder
Build.scientificBuilder

instance ToText Double where
  toText :: Double -> Text
toText = String -> Text
forall a. ToText a => a -> Text
toText (String -> Text) -> (Double -> String) -> Double -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"") ((String -> String) -> String)
-> (Double -> String -> String) -> Double -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Int -> Double -> String -> String
forall a. RealFloat a => Maybe Int -> a -> String -> String
Numeric.showFFloat Maybe Int
forall a. Maybe a
Nothing

instance ToText HTTP.StdMethod where
  toText :: StdMethod -> Text
toText = ByteString -> Text
forall a. ToText a => a -> Text
toText (ByteString -> Text)
-> (StdMethod -> ByteString) -> StdMethod -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StdMethod -> ByteString
HTTP.renderStdMethod

instance ToText (Crypto.Digest a) where
  toText :: Digest a -> Text
toText = ByteString -> Text
forall a. ToText a => a -> Text
toText (ByteString -> Text)
-> (Digest a -> ByteString) -> Digest a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Digest a -> ByteString
forall a. ByteArrayAccess a => a -> ByteString
Bytes.encodeBase16

instance ToText Bool where
  toText :: Bool -> Text
toText Bool
True = Text
"true"
  toText Bool
False = Text
"false"

shortText :: TextBuilder -> Text
shortText :: TextBuilder -> Text
shortText = Text -> Text
LText.toStrict (Text -> Text) -> (TextBuilder -> Text) -> TextBuilder -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> TextBuilder -> Text
Build.toLazyTextWith Int
32

toTextCI :: ToText a => a -> CI Text
toTextCI :: a -> CI Text
toTextCI = Text -> CI Text
forall s. FoldCase s => s -> CI s
CI.mk (Text -> CI Text) -> (a -> Text) -> a -> CI Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Text
forall a. ToText a => a -> Text
toText