{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleInstances #-}


{- | A module providing access to internals (in case you really need them).
Can change at any time, though probably won't.
-}
module Fmt.Internal
(
  -- * Classes
  FormatAsHex(..),
  FormatAsBase64(..),

  -- * Reexports
  module Fmt.Internal.Core,
  module Fmt.Internal.Formatters,
  module Fmt.Internal.Template,
  module Fmt.Internal.Tuple,
  module Fmt.Internal.Numeric,
  module Fmt.Internal.Generic,
)
where


-- Text
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy.Encoding as TL
-- 'Buildable' and raw 'Builder' formatters
import qualified Formatting.Internal.Raw as F
-- Text 'Builder'
import           Data.Text.Lazy.Builder hiding (fromString)
-- Bytestring
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
-- Formatting bytestrings
import qualified Data.ByteString.Builder         as BB
import qualified Data.ByteString.Base64          as B64
import qualified Data.ByteString.Base64.Lazy     as B64L
import qualified Data.ByteString.Base64.URL      as B64U
import qualified Data.ByteString.Base64.URL.Lazy as B64UL

import Fmt.Internal.Core
import Fmt.Internal.Formatters
import Fmt.Internal.Template
import Fmt.Internal.Tuple
import Fmt.Internal.Numeric
import Fmt.Internal.Generic

-- $setup
-- >>> import Fmt

----------------------------------------------------------------------------
-- Hex
----------------------------------------------------------------------------

class FormatAsHex a where
  {- |
Format a number or bytestring as hex:

>>> hexF 3635
"e33"
>>> hexF ("\0\50\63\80" :: BS.ByteString)
"00323f50"
  -}
  hexF :: a -> Builder

instance FormatAsHex BS.ByteString where
  hexF :: ByteString -> Builder
hexF = Text -> Builder
fromLazyText (Text -> Builder) -> (ByteString -> Text) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
TL.decodeLatin1 (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> ByteString
BB.toLazyByteString (Builder -> ByteString)
-> (ByteString -> Builder) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Builder
BB.byteStringHex

instance FormatAsHex BSL.ByteString where
  hexF :: ByteString -> Builder
hexF = Text -> Builder
fromLazyText (Text -> Builder) -> (ByteString -> Text) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
TL.decodeLatin1 (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> ByteString
BB.toLazyByteString (Builder -> ByteString)
-> (ByteString -> Builder) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Builder
BB.lazyByteStringHex

instance {-# OVERLAPPABLE #-} Integral a => FormatAsHex a where
  hexF :: a -> Builder
hexF a
i = Builder
sgn Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> a -> Builder
forall a. Integral a => a -> Builder
F.hex (a -> a
forall a. Num a => a -> a
abs a
i)
    where
      sgn :: Builder
sgn = if a
ia -> a -> Bool
forall a. Ord a => a -> a -> Bool
<a
0 then Builder
"-" else Builder
""

----------------------------------------------------------------------------
-- Base64
----------------------------------------------------------------------------

class FormatAsBase64 a where
  {- |
Convert a bytestring to base64:

>>> base64F ("\0\50\63\80" :: BS.ByteString)
"ADI/UA=="
  -}
  base64F :: a -> Builder
  {- |
Convert a bytestring to base64url (a variant of base64 which omits @\/@ and
thus can be used in URLs):

>>> base64UrlF ("\0\50\63\80" :: BS.ByteString)
"ADI_UA=="
  -}
  base64UrlF :: a -> Builder

instance FormatAsBase64 BS.ByteString where
  base64F :: ByteString -> Builder
base64F    = Text -> Builder
fromText (Text -> Builder) -> (ByteString -> Text) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
T.decodeLatin1 (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
B64.encode
  base64UrlF :: ByteString -> Builder
base64UrlF = Text -> Builder
fromText (Text -> Builder) -> (ByteString -> Text) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
T.decodeLatin1 (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
B64U.encode

instance FormatAsBase64 BSL.ByteString where
  base64F :: ByteString -> Builder
base64F    = Text -> Builder
fromLazyText (Text -> Builder) -> (ByteString -> Text) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
TL.decodeLatin1 (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
B64L.encode
  base64UrlF :: ByteString -> Builder
base64UrlF = Text -> Builder
fromLazyText (Text -> Builder) -> (ByteString -> Text) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
TL.decodeLatin1 (ByteString -> Text)
-> (ByteString -> ByteString) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
B64UL.encode