-- |
-- 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 :: String -> Maybe a
maybeRead = [a] -> Maybe a
forall a. [a] -> Maybe a
listToMaybe ([a] -> Maybe a) -> (String -> [a]) -> String -> Maybe a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((a, String) -> a) -> [(a, String)] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map (a, String) -> a
forall a b. (a, b) -> a
fst ([(a, String)] -> [a])
-> (String -> [(a, String)]) -> String -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [(a, String)]
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 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
len = String
s
                     | Bool
otherwise    = Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
width Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
len) Char
c String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s
  where
  len :: Int
len = String -> Int
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 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
len = String
s
                    | Bool
otherwise    = String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
width Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
len) Char
c
  where
  len :: Int
len = String -> Int
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 :: Int -> a -> String
showWithBase Int
b a
0 = String
"0"
showWithBase Int
b a
n | a
n a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
0     = Char
'-'Char -> String -> String
forall a. a -> [a] -> [a]
:Int -> a -> String
forall a. Integral a => Int -> a -> String
showWithBase Int
b (a -> a
forall a. Num a => a -> a
abs a
n)
                 | Bool
otherwise = (Int -> Char) -> [Int] -> String
forall a b. (a -> b) -> [a] -> [b]
map Int -> Char
intToDigit ([Int] -> String) -> [Int] -> String
forall a b. (a -> b) -> a -> b
$ Int -> a -> [Int]
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 :: Int -> a -> [Int]
integerToDigits Int
b =
    (a -> Int) -> [a] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral
  ([a] -> [Int]) -> (a -> [a]) -> a -> [Int]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> [a]
forall a. [a] -> [a]
reverse
  ([a] -> [a]) -> (a -> [a]) -> a -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Maybe (a, a)) -> a -> [a]
forall b a. (b -> Maybe (a, b)) -> b -> [a]
unfoldr (\a
n -> [(a, a)] -> Maybe (a, a)
forall a. [a] -> Maybe a
listToMaybe [(a, a) -> (a, a)
forall b a. (b, a) -> (a, b)
swap ((a, a) -> (a, a)) -> (a, a) -> (a, a)
forall a b. (a -> b) -> a -> b
$ a
n a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
`divMod` Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
b | a
n a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/= a
0])
  (a -> [a]) -> (a -> a) -> a -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> a
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 :: 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 = (Int -> Char) -> [Int] -> String
forall a b. (a -> b) -> [a] -> [b]
map Int -> Char
intToDigit

theLast :: Int -> [a] -> [a]
theLast :: Int -> [a] -> [a]
theLast Int
n [a]
xs = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop ([a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
xs Int -> Int -> Int
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 :: [a] -> [a]
loop [] = []
loop [a]
xs = [a] -> [a]
forall a. [a] -> [a]
cycle [a]
xs

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