packages feed

pgdl-9.3: src/Utils.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}

module Utils 
where

import qualified Data.Text as T
import Data.Text (Text)
import Text.Printf
import System.IO
import qualified Graphics.Text.Width as TW

-- | give a file size in bytes, return pretty file size 
-- represent in KB, MB, GB or TB
friendlySize :: Integer -> String
friendlySize b
    | fb < 1024 = s2s fb ++ " B "
    | kb < 1024 = s2s kb ++ " KB"
    | mb < 1024 = s2s mb ++ " MB"
    | gb < 1024 = s2s gb ++ " GB"
    | otherwise = s2s tb ++ " TB"
    where
    fb :: Double
    fb = fromIntegral b
    kb = fb / 1024.0
    mb = kb / 1024.0
    gb = mb / 1024.0
    tb = gb / 1024.0
    s2s :: Double -> String
    s2s = printf "%6s" . (printf "%.1f" :: Double -> String)

terminalWidth :: Num a => a
terminalWidth = 80

-- | charDisplayLen returns the display length of a character
charDisplayLen :: Char -> Integer
charDisplayLen c
    | w > 0 = fromIntegral w
    | otherwise = 2 -- assume unknown characters occupy 2 widths
    where
    w = TW.safeWcwidth c

-- | return the estimated display length of a Text
displayLength :: Text -> Integer
displayLength = sum . map charDisplayLen . T.unpack

-- | given a line of Text, cut it to a group of Text
-- which have 'len' display length
cutTextByDisplayLength :: Integer -> Text -> [Text]
cutTextByDisplayLength len = map T.pack . gp . T.unpack
    where
    gp :: String -> [String]
    gp = go 0 "" . map (\c -> (c, charDisplayLen c))
        where
        go :: Integer -> String -> [(Char, Integer)] -> [String]
        go _ acc []
            | null acc = []
            | otherwise = [reverse acc]
        go lsum acc str@(x:xs) 
            | lsum + snd x <= len = go (lsum + snd x) (fst x : acc) xs
            | otherwise = reverse acc : go 0 "" str

askPassword :: IO Text
askPassword = do
    putStr "please input password: "
    hFlush stdout
    hSetEcho stdin False
    pass <- getLine
    hSetEcho stdin True
    putChar '\n'
    return $ T.pack pass

placeTextIntoRectangle :: Int -> -- ^ height
                          Int -> -- ^ width
                          Text -> Text
placeTextIntoRectangle h w t 
    | l > w = error "placeTextIntoRectangle: text too long"
    | otherwise = T.unlines $
        replicate (h `div` 2) (T.replicate w " ") ++
        [T.concat [T.replicate leftspace " ", t, T.replicate rightspace " "]] ++
        replicate (h `div` 2) (T.replicate w " ")
    where
    l = fromIntegral $ displayLength t
    leftspace = (w-l) `div` 2
    rightspace = w - leftspace - l