{-# LANGUAGE OverloadedStrings, RelaxedPolyRec #-}

-- |
-- Module      : Data.Text.Format
-- Copyright   : (c) 2011 MailRank, Inc.
--
-- License     : BSD-style
-- Maintainer  : bos@serpentine.com
-- Stability   : experimental
-- Portability : GHC
--
-- Fast, efficient, flexible support for formatting text strings.

module Data.Text.Format
    (
    -- * Types
      Format
    , Only(..)
    -- ** Types for format control
    , Shown(..)
    -- * Rendering
    , format
    , print
    , hprint
    , build
    -- * Format control
    , left
    , right
    -- ** Integers
    , hex
    -- ** Floating point numbers
    , expt
    , fixed
    , prec
    , shortest
    ) where

import Data.Semigroup ((<>))
import Control.Monad.IO.Class (MonadIO(liftIO))
import Data.Text.Format.Params (Params(..))
import Data.Text.Format.Types.Internal (Format(..), Only(..), Shown(..))
import Data.Text.Format.Types.Internal (Hex(..))
import Data.Text.Lazy.Builder
import Prelude hiding (exp, print)
import System.IO (Handle)
import qualified Data.Double.Conversion.Text as C
import qualified Data.Text as ST
import qualified Data.Text.Buildable as B
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.IO as LT

-- Format strings are almost always constants, and they're expensive
-- to interpret (which we refer to as "cracking" here).  We'd really
-- like to have GHC memoize the cracking of a known-constant format
-- string, so that it occurs at most once.
--
-- To achieve this, we arrange to have the cracked version of a format
-- string let-floated out as a CAF, by inlining the definitions of
-- build and functions that invoke it.  This works well with GHC 7.

-- | Render a format string and arguments to a 'Builder'.
build :: Params ps => Format -> ps -> Builder
build :: forall ps. Params ps => Format -> ps -> Builder
build Format
fmt ps
ps = [Builder] -> [Builder] -> Builder
zipParams (Format -> [Builder]
crack Format
fmt) (forall ps. Params ps => ps -> [Builder]
buildParams ps
ps)
{-# INLINE build #-}

zipParams :: [Builder] -> [Builder] -> Builder
zipParams :: [Builder] -> [Builder] -> Builder
zipParams [Builder]
fragments [Builder]
params = forall {a}. Semigroup a => [a] -> [a] -> a
go [Builder]
fragments [Builder]
params
  where go :: [a] -> [a] -> a
go (a
f:[a]
fs) (a
y:[a]
ys) = a
f forall a. Semigroup a => a -> a -> a
<> a
y forall a. Semigroup a => a -> a -> a
<> [a] -> [a] -> a
go [a]
fs [a]
ys
        go [a
f] []        = a
f
        go [a]
_ [a]
_ = forall a. HasCallStack => [Char] -> a
error forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Char]
LT.unpack forall a b. (a -> b) -> a -> b
$ forall ps. Params ps => Format -> ps -> Text
format
                 Format
"Data.Text.Format.build: {} sites, but {} parameters"
                 (forall (t :: * -> *) a. Foldable t => t a -> Int
length [Builder]
fragments forall a. Num a => a -> a -> a
- Int
1, forall (t :: * -> *) a. Foldable t => t a -> Int
length [Builder]
params)

crack :: Format -> [Builder]
crack :: Format -> [Builder]
crack = forall a b. (a -> b) -> [a] -> [b]
map Text -> Builder
fromText forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> [Text]
ST.splitOn Text
"{}" forall b c a. (b -> c) -> (a -> b) -> a -> c
. Format -> Text
fromFormat

-- | Render a format string and arguments to a 'LT.Text'.
format :: Params ps => Format -> ps -> LT.Text
format :: forall ps. Params ps => Format -> ps -> Text
format Format
fmt ps
ps = Builder -> Text
toLazyText forall a b. (a -> b) -> a -> b
$ forall ps. Params ps => Format -> ps -> Builder
build Format
fmt ps
ps
{-# INLINE format #-}

-- | Render a format string and arguments, then print the result.
print :: (MonadIO m, Params ps) => Format -> ps -> m ()
print :: forall (m :: * -> *) ps.
(MonadIO m, Params ps) =>
Format -> ps -> m ()
print Format
fmt ps
ps = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> IO ()
LT.putStr forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
toLazyText forall a b. (a -> b) -> a -> b
$ forall ps. Params ps => Format -> ps -> Builder
build Format
fmt ps
ps
{-# INLINE print #-}

-- | Render a format string and arguments, then print the result to
-- the given file handle.
hprint :: (MonadIO m, Params ps) => Handle -> Format -> ps -> m ()
hprint :: forall (m :: * -> *) ps.
(MonadIO m, Params ps) =>
Handle -> Format -> ps -> m ()
hprint Handle
h Format
fmt ps
ps = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handle -> Text -> IO ()
LT.hPutStr Handle
h forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
toLazyText forall a b. (a -> b) -> a -> b
$ forall ps. Params ps => Format -> ps -> Builder
build Format
fmt ps
ps
{-# INLINE hprint #-}

-- | Pad the left hand side of a string until it reaches @k@
-- characters wide, if necessary filling with character @c@.
left :: B.Buildable a => Int -> Char -> a -> Builder
left :: forall a. Buildable a => Int -> Char -> a -> Builder
left Int
k Char
c =
    Text -> Builder
fromLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Char -> Text -> Text
LT.justifyRight (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
k) Char
c forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall p. Buildable p => p -> Builder
B.build

-- | Pad the right hand side of a string until it reaches @k@
-- characters wide, if necessary filling with character @c@.
right :: B.Buildable a => Int -> Char -> a -> Builder
right :: forall a. Buildable a => Int -> Char -> a -> Builder
right Int
k Char
c =
    Text -> Builder
fromLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Char -> Text -> Text
LT.justifyLeft (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
k) Char
c forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall p. Buildable p => p -> Builder
B.build

-- | Render a floating point number, with the given number of digits
-- of precision.  Uses decimal notation for values between @0.1@ and
-- @9,999,999@, and scientific notation otherwise.
prec :: (Real a) =>
        Int
     -- ^ Number of digits of precision.
     -> a -> Builder
{-# RULES "prec/Double"
    forall d x. prec d (x::Double) = B.build (C.toPrecision d x) #-}
prec :: forall a. Real a => Int -> a -> Builder
prec Int
digits = forall p. Buildable p => p -> Builder
B.build forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Double -> Text
C.toPrecision Int
digits forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Real a, Fractional b) => a -> b
realToFrac
{-# NOINLINE[0] prec #-}

-- | Render a floating point number using normal notation, with the
-- given number of decimal places.
fixed :: (Real a) =>
         Int
      -- ^ Number of digits of precision after the decimal.
      -> a -> Builder
fixed :: forall a. Real a => Int -> a -> Builder
fixed Int
decs = forall p. Buildable p => p -> Builder
B.build forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Double -> Text
C.toFixed Int
decs forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Real a, Fractional b) => a -> b
realToFrac
{-# RULES "fixed/Double"
    forall d x. fixed d (x::Double) = B.build (C.toFixed d x) #-}
{-# NOINLINE[0] fixed #-}

-- | Render a floating point number using scientific/engineering
-- notation (e.g. @2.3e123@), with the given number of decimal places.
expt :: (Real a) =>
        Int
     -- ^ Number of digits of precision after the decimal.
     -> a -> Builder
expt :: forall a. Real a => Int -> a -> Builder
expt Int
decs = forall p. Buildable p => p -> Builder
B.build forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Double -> Text
C.toExponential Int
decs forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Real a, Fractional b) => a -> b
realToFrac
{-# RULES "expt/Double"
    forall d x. expt d (x::Double) = B.build (C.toExponential d x) #-}
{-# NOINLINE[0] expt #-}

-- | Render a floating point number using the smallest number of
-- digits that correctly represent it.
shortest :: (Real a) => a -> Builder
shortest :: forall a. Real a => a -> Builder
shortest = forall p. Buildable p => p -> Builder
B.build forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Text
C.toShortest forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Real a, Fractional b) => a -> b
realToFrac
{-# RULES "shortest/Double"
    forall x. shortest (x::Double) = B.build (C.toShortest x) #-}
{-# NOINLINE[0] shortest #-}

-- | Render an integer using hexadecimal notation.  (No leading "0x"
-- is added.)
hex :: Integral a => a -> Builder
hex :: forall a. Integral a => a -> Builder
hex = forall p. Buildable p => p -> Builder
B.build forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Hex a
Hex
{-# INLINE hex #-}