ordinal-0.6.0.0: app/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Data.Char (digitToInt, isDigit)
import Data.Default.Class (Default (def))
import Data.HashMap.Strict (HashMap, fromList, lookup)
import Data.Maybe (fromMaybe)
import Data.Text (Text, pack, toCaseFold)
import qualified Data.Text.IO as TI
import System.Console.GetOpt (ArgDescr (NoArg, ReqArg), ArgOrder (RequireOrder), OptDescr (Option), getOpt, usageInfo)
import System.Environment (getArgs)
import Text.Numerals.Algorithm (NumeralsAlgorithm)
import Text.Numerals.Class (toCardinal, toOrdinal, toShortOrdinal, toTimeText')
import Text.Numerals.Languages (dutch, english, french, german)
import Prelude hiding (lookup)
languages_ :: [([Text], NumeralsAlgorithm)]
languages_ =
[ (["nl", "nld", "dut", "dutch"], dutch),
(["en", "eng", "english"], english),
(["fr", "fra", "fre", "french"], french),
(["de", "deu", "ger", "german"], german)
]
languages :: HashMap Text NumeralsAlgorithm
languages = fromList [(toCaseFold k, v) | (ks, v) <- languages_, k <- ks]
data OrdinalMode = Cardinal | Ordinal | ShortOrdinal | Time deriving (Eq, Ord, Read, Show)
data OrdinalConfig = OrdinalConfig {lang :: NumeralsAlgorithm, mode :: OrdinalMode, help :: Bool}
instance Default OrdinalMode where
def = Cardinal
instance Default OrdinalConfig where
def = OrdinalConfig english def False
determine :: OrdinalMode -> NumeralsAlgorithm -> Integer -> Text
determine Cardinal = toCardinal
determine Ordinal = toOrdinal
determine ShortOrdinal = toShortOrdinal
determine Time = go
where
go lng x = toTimeText' lng (fromIntegral (x `div` 60)) (fromIntegral (x `mod` 60))
findLanguage :: String -> IO NumeralsAlgorithm
findLanguage k =
case lookup (toCaseFold (pack k)) languages of
Just x -> pure x
_ -> fail ("Can not find language \"" ++ k ++ "\".")
options :: [OptDescr (OrdinalConfig -> IO OrdinalConfig)]
options =
[ Option "l" ["lang", "language"] (ReqArg (\l' v -> (\l -> v {lang = l}) <$> findLanguage l') "lang") "specify the language, English by default",
Option "c" ["cardinal"] (NoArg (\v -> pure (v {mode = Cardinal}))) "set the number mode to cardinal numbers, which is the deault",
Option "o" ["ordinal"] (NoArg (\v -> pure (v {mode = Ordinal}))) "set the number mode to ordinal numbers",
Option "s" ["short-ordinal"] (NoArg (\v -> pure (v {mode = ShortOrdinal}))) "set the number mode to short ordinal numbers",
Option "t" ["time"] (NoArg (\v -> pure (v {mode = Time}))) "set the number mode to time",
Option "?h" ["help"] (NoArg (\v -> pure (v {help = True}))) "show this help page"
]
header :: String
header = "Usage: ordinal [OPTION...] numbers..."
compilerOpts :: [String] -> IO ([OrdinalConfig -> IO OrdinalConfig], [String])
compilerOpts argv =
case getOpt RequireOrder options argv of
(o, n, []) -> return (o, n)
(_, _, errs) -> ioError (userError (concat errs ++ usageInfo header options))
readInt :: String -> Integer
readInt v = fromMaybe (read v) (go 0 v)
where
go h (':' : xs) = Just (60 * h + read xs)
go h (x : xs) | isDigit x = go (h * 10 + fromIntegral (digitToInt x)) xs
go _ _ = Nothing
main :: IO ()
main = do
args <- getArgs
~(opts, xs) <- compilerOpts args
opt <- foldl (>>=) def opts
if help opt
then putStrLn (usageInfo header options)
else mapM_ (TI.putStrLn . determine (mode opt) (lang opt) . readInt) xs