{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}
-- |
-- Module      : Formatting.Formatters
-- Copyright   : (c) 2013 Chris Done, 2013 Shachaf Ben-Kiki
-- License     : BSD3
-- Maintainer  : alex@farfromthere.net
-- Stability   : experimental
-- Portability : GHC
--
-- Formatting functions.

module Formatting.Formatters
  (
  -- * Text/string types
  text,
  stext,
  string,
  shown,
  char,
  builder,
  fconst,
  -- * Numbers
  int,
  float,
  fixed,
  sci,
  scifmt,
  shortest,
  groupInt,
  commas,
  ords,
  plural,
  asInt,
  -- * Padding
  left,
  right,
  center,
  fitLeft,
  fitRight,
  -- * Bases
  base,
  bin,
  oct,
  hex,
  prefixBin,
  prefixOct,
  prefixHex,
  bytes,
  -- * Buildables
  build,
  Buildable,
  ) where

import           Formatting.Internal

import           Data.Char (chr, ord)
import           Data.Scientific
import qualified Data.Text as S
import qualified Data.Text as T
import           Formatting.Buildable (Buildable)
import qualified Formatting.Buildable as B (build)
import qualified Data.Text.Format as T
import           Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as LT
import           Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.Lazy.Builder as T
import           Data.Text.Lazy.Builder.Scientific
import           Numeric (showIntAtBase)

-- | Output a lazy text.
text :: Format r (Text -> r)
text :: Format r (Text -> r)
text = (Text -> Builder) -> Format r (Text -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later Text -> Builder
T.fromLazyText
{-# INLINE text #-}

-- | Output a strict text.
stext :: Format r (S.Text -> r)
stext :: Format r (Text -> r)
stext = (Text -> Builder) -> Format r (Text -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later Text -> Builder
T.fromText
{-# INLINE stext #-}

-- | Output a string.
string :: Format r (String -> r)
string :: Format r (String -> r)
string = (String -> Builder) -> Format r (String -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Text -> Builder
T.fromText (Text -> Builder) -> (String -> Text) -> String -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack)
{-# INLINE string #-}

-- | Output a showable value (instance of 'Show') by turning it into
-- 'Text':
--
-- >>> format ("Value number " % shown % " is " % shown % ".") 42 False
-- "Value number 42 is False."
shown :: Show a => Format r (a -> r)
shown :: Format r (a -> r)
shown = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Text -> Builder
T.fromText (Text -> Builder) -> (a -> Text) -> a -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> (a -> String) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String
forall a. Show a => a -> String
show)
{-# INLINE shown #-}

-- | Output a character.
char :: Format r (Char -> r)
char :: Format r (Char -> r)
char = (Char -> Builder) -> Format r (Char -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later Char -> Builder
forall p. Buildable p => p -> Builder
B.build
{-# INLINE char #-}

-- | Build a builder.
builder :: Format r (Builder -> r)
builder :: Format r (Builder -> r)
builder = (Builder -> Builder) -> Format r (Builder -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later Builder -> Builder
forall a. a -> a
id
{-# INLINE builder #-}

-- | Like `const` but for formatters.
fconst :: Builder -> Format r (a -> r)
fconst :: Builder -> Format r (a -> r)
fconst Builder
m = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Builder -> a -> Builder
forall a b. a -> b -> a
const Builder
m)
{-# INLINE fconst #-}

-- | Build anything that implements the "Buildable" class.
build :: Buildable a => Format r (a -> r)
build :: Format r (a -> r)
build = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later a -> Builder
forall p. Buildable p => p -> Builder
B.build
{-# INLINE build #-}

-- | Render an integral e.g. 123 -> \"123\", 0 -> \"0\".
int :: Integral a => Format r (a -> r)
int :: Format r (a -> r)
int = Int -> Format r (a -> r)
forall a r. Integral a => Int -> Format r (a -> r)
base Int
10
{-# INLINE int #-}

-- | Render some floating point with the usual notation, e.g. 123.32 => \"123.32\"
float :: Real a => Format r (a -> r)
float :: Format r (a -> r)
float = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later a -> Builder
forall a. Real a => a -> Builder
T.shortest
{-# INLINE float #-}

-- | Render a floating point number using normal notation, with the
-- given number of decimal places.
fixed :: Real a => Int -> Format r (a -> r)
fixed :: Int -> Format r (a -> r)
fixed Int
i = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Int -> a -> Builder
forall a. Real a => Int -> a -> Builder
T.fixed Int
i)
{-# INLINE fixed #-}

-- | Render a floating point number using the smallest number of
-- digits that correctly represent it. Note that in the case of whole
-- numbers it will still add one decimal place, e.g. "1.0".
shortest :: Real a => Format r (a -> r)
shortest :: Format r (a -> r)
shortest = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later a -> Builder
forall a. Real a => a -> Builder
T.shortest
{-# INLINE shortest #-}

-- | Render a scientific number.
sci :: Format r (Scientific -> r)
sci :: Format r (Scientific -> r)
sci = (Scientific -> Builder) -> Format r (Scientific -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later Scientific -> Builder
scientificBuilder
{-# INLINE sci #-}

-- | Render a scientific number with options.
scifmt :: FPFormat -> Maybe Int -> Format r (Scientific -> r)
scifmt :: FPFormat -> Maybe Int -> Format r (Scientific -> r)
scifmt FPFormat
f Maybe Int
i = (Scientific -> Builder) -> Format r (Scientific -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (FPFormat -> Maybe Int -> Scientific -> Builder
formatScientificBuilder FPFormat
f Maybe Int
i)
{-# INLINE scifmt #-}

-- | Shows the Int value of Enum instances using 'fromEnum'.
--
-- >>> format ("Got: " % char % " (" % asInt % ")") 'a' 'a'
-- "Got: a (97)"
asInt :: Enum a => Format r (a -> r)
asInt :: Format r (a -> r)
asInt = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Int -> Builder
forall a. Real a => a -> Builder
T.shortest (Int -> Builder) -> (a -> Int) -> a -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Int
forall a. Enum a => a -> Int
fromEnum)
{-# INLINE asInt #-}

-- | Pad the left hand side of a string until it reaches k characters
-- wide, if necessary filling with character c.
left :: Buildable a => Int -> Char -> Format r (a -> r)
left :: Int -> Char -> Format r (a -> r)
left Int
i Char
c = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Int -> Char -> a -> Builder
forall a. Buildable a => Int -> Char -> a -> Builder
T.left Int
i Char
c)
{-# INLINE left #-}

-- | Pad the right hand side of a string until it reaches k characters
-- wide, if necessary filling with character c.
right :: Buildable a => Int -> Char -> Format r (a -> r)
right :: Int -> Char -> Format r (a -> r)
right Int
i Char
c = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Int -> Char -> a -> Builder
forall a. Buildable a => Int -> Char -> a -> Builder
T.right Int
i Char
c)
{-# INLINE right #-}

-- | Pad the left & right hand side of a string until it reaches k characters
-- wide, if necessary filling with character c.
center :: Buildable a => Int -> Char -> Format r (a -> r)
center :: Int -> Char -> Format r (a -> r)
center Int
i Char
c = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later a -> Builder
centerT where
  centerT :: a -> Builder
centerT = Text -> Builder
T.fromLazyText (Text -> Builder) -> (a -> Text) -> a -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Char -> Text -> Text
LT.center (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
i) Char
c (Text -> Text) -> (a -> Text) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
T.toLazyText (Builder -> Text) -> (a -> Builder) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Builder
forall p. Buildable p => p -> Builder
B.build

-- | Group integral numbers, e.g. groupInt 2 '.' on 123456 -> \"12.34.56\".
groupInt :: (Buildable n,Integral n) => Int -> Char -> Format r (n -> r)
groupInt :: Int -> Char -> Format r (n -> r)
groupInt Int
0 Char
_ = (n -> Builder) -> Format r (n -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later n -> Builder
forall p. Buildable p => p -> Builder
B.build
groupInt Int
i Char
c =
  (n -> Builder) -> Format r (n -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later
    (\n
n ->
       if n
n n -> n -> Bool
forall a. Ord a => a -> a -> Bool
< n
0
         then Builder
"-" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> n -> Builder
commaize (n -> n
forall a. Num a => a -> a
negate n
n)
         else n -> Builder
commaize n
n)
  where
    commaize :: n -> Builder
commaize =
      Text -> Builder
T.fromLazyText (Text -> Builder) -> (n -> Text) -> n -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
      Text -> Text
LT.reverse (Text -> Text) -> (n -> Text) -> n -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
      ((Char, Char) -> Text -> Text) -> Text -> [(Char, Char)] -> Text
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (Char, Char) -> Text -> Text
merge Text
"" ([(Char, Char)] -> Text) -> (n -> [(Char, Char)]) -> n -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
      Text -> Text -> [(Char, Char)]
LT.zip (Text
zeros Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
forall t. Semigroup t => t -> t
cycle' Text
zeros') (Text -> [(Char, Char)]) -> (n -> Text) -> n -> [(Char, Char)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
LT.reverse (Text -> Text) -> (n -> Text) -> n -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
T.toLazyText (Builder -> Text) -> (n -> Builder) -> n -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> Builder
forall p. Buildable p => p -> Builder
B.build
    zeros :: Text
zeros = Int64 -> Text -> Text
LT.replicate (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
i) (Char -> Text
LT.singleton Char
'0')
    zeros' :: Text
zeros' = Char -> Text
LT.singleton Char
c Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
LT.tail Text
zeros
    merge :: (Char, Char) -> Text -> Text
merge (Char
f, Char
c') Text
rest
      | Char
f Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
c = Char -> Text
LT.singleton Char
c Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Char -> Text
LT.singleton Char
c' Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
rest
      | Bool
otherwise = Char -> Text
LT.singleton Char
c' Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
rest
    cycle' :: t -> t
cycle' t
xs = t
xs t -> t -> t
forall a. Semigroup a => a -> a -> a
<> t -> t
cycle' t
xs

-- | Fit in the given length, truncating on the left.
fitLeft :: Buildable a => Int -> Format r (a -> r)
fitLeft :: Int -> Format r (a -> r)
fitLeft Int
size = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Int64 -> a -> Builder
forall p. Buildable p => Int64 -> p -> Builder
fit (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size)) where
  fit :: Int64 -> p -> Builder
fit Int64
i = Text -> Builder
T.fromLazyText (Text -> Builder) -> (p -> Text) -> p -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Text -> Text
LT.take Int64
i (Text -> Text) -> (p -> Text) -> p -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
T.toLazyText (Builder -> Text) -> (p -> Builder) -> p -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. p -> Builder
forall p. Buildable p => p -> Builder
B.build

-- | Fit in the given length, truncating on the right.
fitRight :: Buildable a => Int -> Format r (a -> r)
fitRight :: Int -> Format r (a -> r)
fitRight Int
size = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (Int64 -> a -> Builder
forall p. Buildable p => Int64 -> p -> Builder
fit (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
size)) where
  fit :: Int64 -> p -> Builder
fit Int64
i = Text -> Builder
T.fromLazyText (Text -> Builder) -> (p -> Text) -> p -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
          (\Text
t -> Int64 -> Text -> Text
LT.drop (Text -> Int64
LT.length Text
t Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
- Int64
i) Text
t)
          (Text -> Text) -> (p -> Text) -> p -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
T.toLazyText
          (Builder -> Text) -> (p -> Builder) -> p -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. p -> Builder
forall p. Buildable p => p -> Builder
B.build

-- | Add commas to an integral, e.g 12000 -> \ "12,000".
commas :: (Buildable n,Integral n) => Format r (n -> r)
commas :: Format r (n -> r)
commas = Int -> Char -> Format r (n -> r)
forall n r.
(Buildable n, Integral n) =>
Int -> Char -> Format r (n -> r)
groupInt Int
3 Char
','
{-# INLINE commas #-}

-- | Add a suffix to an integral, e.g. 1st, 2nd, 3rd, 21st.
ords :: Integral n => Format r (n -> r)
ords :: Format r (n -> r)
ords = (n -> Builder) -> Format r (n -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later n -> Builder
forall a. Integral a => a -> Builder
go
  where go :: a -> Builder
go a
n
          | a
tens a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
3 Bool -> Bool -> Bool
&& a
tens a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
21 = Int -> a -> Builder
forall a. Real a => Int -> a -> Builder
T.fixed Int
0 a
n Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"th"
          | Bool
otherwise =
            Int -> a -> Builder
forall a. Real a => Int -> a -> Builder
T.fixed Int
0 a
n Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
            case a
n a -> a -> a
forall a. Integral a => a -> a -> a
`mod` a
10 of
              a
1 -> Builder
"st"
              a
2 -> Builder
"nd"
              a
3 -> Builder
"rd"
              a
_ -> Builder
"th"
          where tens :: a
tens = a
n a -> a -> a
forall a. Integral a => a -> a -> a
`mod` a
100

-- | English plural suffix for an integral.
--
-- For example:
--
-- >>> set -XOverloadedStrings
-- >>> formatPeople = format (int % " " <> plural "person" "people" % ".") :: Int -> Data.Text.Lazy.Text
-- >>> formatPeople 1
-- "1 person."
-- >>> formatPeople 3
-- "3 people."
plural :: (Num a, Eq a) => Text -> Text -> Format r (a -> r)
plural :: Text -> Text -> Format r (a -> r)
plural Text
s Text
p = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (\a
i -> if a
i a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
1 then Text -> Builder
forall p. Buildable p => p -> Builder
B.build Text
s else Text -> Builder
forall p. Buildable p => p -> Builder
B.build Text
p)

-- | Render an integral at base n.
base :: Integral a => Int -> Format r (a -> r)
base :: Int -> Format r (a -> r)
base Int
numBase = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later (String -> Builder
forall p. Buildable p => p -> Builder
B.build (String -> Builder) -> (a -> String) -> a -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> a -> String
forall a. Integral a => Int -> a -> String
atBase Int
numBase)
{-# INLINE base #-}

-- | Render an integer using binary notation. (No leading 0b is
-- added.) Defined as @bin = 'base' 2@.
bin :: Integral a => Format r (a -> r)
bin :: Format r (a -> r)
bin = Int -> Format r (a -> r)
forall a r. Integral a => Int -> Format r (a -> r)
base Int
2
{-# INLINE bin #-}

-- | Render an integer using octal notation. (No leading 0o is
-- added.) Defined as @oct = 'base' 8@.
oct :: Integral a => Format r (a -> r)
oct :: Format r (a -> r)
oct = Int -> Format r (a -> r)
forall a r. Integral a => Int -> Format r (a -> r)
base Int
8
{-# INLINE oct #-}

-- | Render an integer using hexadecimal notation. (No leading 0x is
-- added.) Has a specialized implementation.
hex :: Integral a => Format r (a -> r)
hex :: Format r (a -> r)
hex = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later a -> Builder
forall a. Integral a => a -> Builder
T.hex
{-# INLINE hex #-}

-- | Render an integer using binary notation with a leading 0b.
--
-- See also 'Formatting.Combinators.binPrefix' for fixed-width formatting.
prefixBin :: Integral a => Format r (a -> r)
prefixBin :: Format r (a -> r)
prefixBin = Format (a -> r) (a -> r)
"0b" Format (a -> r) (a -> r) -> Format r (a -> r) -> Format r (a -> r)
forall r a r'. Format r a -> Format r' r -> Format r' a
% Format r (a -> r)
forall a r. Integral a => Format r (a -> r)
bin
{-# INLINE prefixBin #-}

-- | Render an integer using octal notation with a leading 0o.
--
-- See also 'Formatting.Combinators.octPrefix' for fixed-width formatting.
prefixOct :: Integral a => Format r (a -> r)
prefixOct :: Format r (a -> r)
prefixOct = Format (a -> r) (a -> r)
"0o" Format (a -> r) (a -> r) -> Format r (a -> r) -> Format r (a -> r)
forall r a r'. Format r a -> Format r' r -> Format r' a
% Format r (a -> r)
forall a r. Integral a => Format r (a -> r)
oct
{-# INLINE prefixOct #-}

-- | Render an integer using hexadecimal notation with a leading 0x.
--
-- See also 'Formatting.Combinators.hexPrefix' for fixed-width formatting.
prefixHex :: Integral a => Format r (a -> r)
prefixHex :: Format r (a -> r)
prefixHex = Format (a -> r) (a -> r)
"0x" Format (a -> r) (a -> r) -> Format r (a -> r) -> Format r (a -> r)
forall r a r'. Format r a -> Format r' r -> Format r' a
% Format r (a -> r)
forall a r. Integral a => Format r (a -> r)
hex
{-# INLINE prefixHex #-}

-- The following code is mostly taken from `Numeric.Lens.' (from
-- `lens') and modified.

-- | Internal function that converts a number to a base base-2 through
-- base-36.
atBase :: Integral a => Int -> a -> String
atBase :: Int -> a -> String
atBase Int
b a
_ | Int
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
2 Bool -> Bool -> Bool
|| Int
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
36 = String -> String
forall a. HasCallStack => String -> a
error (String
"base: Invalid base " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
b)
atBase Int
b a
n =
  (Integer -> String -> String) -> Integer -> String -> String
forall a.
Real a =>
(a -> String -> String) -> a -> String -> String
showSigned' (Integer -> (Int -> Char) -> Integer -> String -> String
forall a.
(Integral a, Show a) =>
a -> (Int -> Char) -> a -> String -> String
showIntAtBase (Int -> Integer
forall a. Integral a => a -> Integer
toInteger Int
b) Int -> Char
intToDigit') (a -> Integer
forall a. Integral a => a -> Integer
toInteger a
n) String
""
{-# INLINE atBase #-}

-- | A simpler variant of 'Numeric.showSigned' that only prepends a dash and
-- doesn't know about parentheses
showSigned' :: Real a => (a -> ShowS) -> a -> ShowS
showSigned' :: (a -> String -> String) -> a -> String -> String
showSigned' a -> String -> String
f a
n
  | a
n a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
0     = Char -> String -> String
showChar Char
'-' (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String -> String
f (a -> a
forall a. Num a => a -> a
negate a
n)
  | Bool
otherwise = a -> String -> String
f a
n

-- | Like 'Data.Char.intToDigit', but handles up to base-36
intToDigit' :: Int -> Char
intToDigit' :: Int -> Char
intToDigit' Int
i
  | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0  Bool -> Bool -> Bool
&& Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
10 = Int -> Char
chr (Char -> Int
ord Char
'0' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
i)
  | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
10 Bool -> Bool -> Bool
&& Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
36 = Int -> Char
chr (Char -> Int
ord Char
'a' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
10)
  | Bool
otherwise = String -> Char
forall a. HasCallStack => String -> a
error (String
"intToDigit': Invalid int " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
i)

-- | Renders a given byte count using an appropiate decimal binary suffix:
--
-- >>> format (bytes shortest) 1024
-- "1KB"
--
-- >>> format (bytes (fixed 2 % " ")) (1024*1024*5)
-- "5.00 MB"
--
bytes :: (Ord f,Integral a,Fractional f)
      => Format Builder (f -> Builder) -- ^ formatter for the decimal part
      -> Format r (a -> r)
bytes :: Format Builder (f -> Builder) -> Format r (a -> r)
bytes Format Builder (f -> Builder)
d = (a -> Builder) -> Format r (a -> r)
forall a r. (a -> Builder) -> Format r (a -> r)
later a -> Builder
forall a. Integral a => a -> Builder
go
  where go :: a -> Builder
go a
bs =
          Format Builder (f -> Builder) -> f -> Builder
forall a. Format Builder a -> a
bprint Format Builder (f -> Builder)
d (a -> f
forall a b. (Integral a, Num b) => a -> b
fromIntegral (a -> a
forall a. Num a => a -> a
signum a
bs) f -> f -> f
forall a. Num a => a -> a -> a
* f
dec) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Builder]
bytesSuffixes [Builder] -> Int -> Builder
forall a. [a] -> Int -> a
!!
          Int
i
          where (f
dec,Int
i) = a -> (f, Int)
forall a a. (Fractional a, Integral a, Ord a) => a -> (a, Int)
getSuffix (a -> a
forall a. Num a => a -> a
abs a
bs)
        getSuffix :: a -> (a, Int)
getSuffix a
n =
          ((a, Int) -> Bool)
-> ((a, Int) -> (a, Int)) -> (a, Int) -> (a, Int)
forall a. (a -> Bool) -> (a -> a) -> a -> a
until (a, Int) -> Bool
forall a. (Ord a, Num a) => (a, Int) -> Bool
p
                (\(a
x,Int
y) -> (a
x a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
1024,Int
y Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1))
                (a -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
n,Int
0)
          where p :: (a, Int) -> Bool
p (a
n',Int
numDivs) =
                  a
n' a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
1024 Bool -> Bool -> Bool
|| Int
numDivs Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== ([Builder] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Builder]
bytesSuffixes Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
        bytesSuffixes :: [Builder]
bytesSuffixes =
          [Builder
"B",Builder
"KB",Builder
"MB",Builder
"GB",Builder
"TB",Builder
"PB",Builder
"EB",Builder
"ZB",Builder
"YB"]