-- |
-- Module      : Text.PercentFormat
-- Copyright   : (c) 2016-2018 Rudy Matela
-- License     : 3-Clause BSD  (see the file LICENSE)
-- Maintainer  : Rudy Matela <rudy@matela.com.br>
--
-- This file is part of PercentFormat a library for printf-style string
-- formatting.
--
-- This module provides miscellaneous utility functions.
module Text.PercentFormat.Utils
  ( maybeRead
  , align
  , rightAlign
  , leftAlign
  , showWithBase
  , applyWhen
  , intsToDigits
  , theLast
  , loop
  , none
  , integerToDigits
  )
where

-- TODO: document this module more thoroughly

import Data.Maybe (listToMaybe)
import Data.List (unfoldr)
import Data.Char (intToDigit)

-- | Reads a value encoded as a string,
--   return 'Just' the value or 'Nothing' on error.
maybeRead :: Read a => String -> Maybe a
-- TODO: Use readsPrec to implement this.
maybeRead :: forall a. Read a => String -> Maybe a
maybeRead = forall a. [a] -> Maybe a
listToMaybe forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Read a => ReadS a
reads

align :: Bool -> Char -> Int -> String -> String
align :: Bool -> Char -> Int -> String -> String
align Bool
left = if Bool
left
               then Char -> Int -> String -> String
leftAlign
               else Char -> Int -> String -> String
rightAlign

-- | @rightAlign c w s@ aligns 'String' @s@ to the right
--   in a field of width @w@ using 'Char' @c@ as padding.
--
-- > right ' ' 5 "123"
-- "  123"
rightAlign :: Char -> Int -> String -> String
rightAlign :: Char -> Int -> String -> String
rightAlign Char
c Int
width String
s | Int
width forall a. Ord a => a -> a -> Bool
<= Int
len = String
s
                     | Bool
otherwise    = forall a. Int -> a -> [a]
replicate (Int
width forall a. Num a => a -> a -> a
- Int
len) Char
c forall a. [a] -> [a] -> [a]
++ String
s
  where
  len :: Int
len = forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s

-- | @left c w s@ aligns 'String' @s@ to the left
--   in a field of width @w@ using 'Char' @c@ as padding.
--
-- > left ' ' 5 "123"
-- "123  "
leftAlign :: Char -> Int -> String -> String
leftAlign :: Char -> Int -> String -> String
leftAlign Char
c Int
width String
s | Int
width forall a. Ord a => a -> a -> Bool
<= Int
len = String
s
                    | Bool
otherwise    = String
s forall a. [a] -> [a] -> [a]
++ forall a. Int -> a -> [a]
replicate (Int
width forall a. Num a => a -> a -> a
- Int
len) Char
c
  where
  len :: Int
len = forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s

-- | @showWithBase b n@ returns a string representation of @n@ in base @b@.
--
-- > showWithBase 2 10
-- "1010"
-- > showWithBase 16 49406
-- "c0f3"
-- > showWithBase 10 (-1234)
-- "-1234"
showWithBase :: Integral a => Int -> a -> String
showWithBase :: forall a. Integral a => Int -> a -> String
showWithBase Int
b a
0 = String
"0"
showWithBase Int
b a
n | a
n forall a. Ord a => a -> a -> Bool
< a
0     = Char
'-'forall a. a -> [a] -> [a]
:forall a. Integral a => Int -> a -> String
showWithBase Int
b (forall a. Num a => a -> a
abs a
n)
                 | Bool
otherwise = forall a b. (a -> b) -> [a] -> [b]
map Int -> Char
intToDigit forall a b. (a -> b) -> a -> b
$ forall a. Integral a => Int -> a -> [Int]
integerToDigits Int
b a
n

-- | Given an integer, returns a list of digits.  Signal is ignored.
integerToDigits :: Integral a => Int -> a -> [Int]
integerToDigits :: forall a. Integral a => Int -> a -> [Int]
integerToDigits Int
b =
    forall a b. (a -> b) -> [a] -> [b]
map forall a b. (Integral a, Num b) => a -> b
fromIntegral
  forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse
  forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b a. (b -> Maybe (a, b)) -> b -> [a]
unfoldr (\a
n -> forall a. [a] -> Maybe a
listToMaybe [forall {b} {a}. (b, a) -> (a, b)
swap forall a b. (a -> b) -> a -> b
$ a
n forall a. Integral a => a -> a -> (a, a)
`divMod` forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
b | a
n forall a. Eq a => a -> a -> Bool
/= a
0])
  forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Num a => a -> a
abs
  where
  swap :: (b, a) -> (a, b)
swap (b
x,a
y) = (a
y,b
x)  -- not available on Hugs


applyWhen :: Bool -> (a -> a) -> a -> a
applyWhen :: forall a. Bool -> (a -> a) -> a -> a
applyWhen Bool
True  a -> a
f a
x = a -> a
f a
x
applyWhen Bool
False a -> a
f a
x = a
x

intsToDigits :: [Int] -> String
intsToDigits :: [Int] -> String
intsToDigits = forall a b. (a -> b) -> [a] -> [b]
map Int -> Char
intToDigit

theLast :: Int -> [a] -> [a]
theLast :: forall a. Int -> [a] -> [a]
theLast Int
n [a]
xs = forall a. Int -> [a] -> [a]
drop (forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
xs forall a. Num a => a -> a -> a
- Int
n) [a]
xs

-- | Like cycle, but return an empty list when the source list is empty.
loop :: [a] -> [a]
loop :: forall a. [a] -> [a]
loop [] = []
loop [a]
xs = forall a. [a] -> [a]
cycle [a]
xs

none :: (a -> Bool) -> [a] -> Bool
none :: forall a. (a -> Bool) -> [a] -> Bool
none a -> Bool
p = Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *). Foldable t => t Bool -> Bool
or forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map a -> Bool
p