packages feed

Monadoro-0.3.0.0: app/Main.hs

module Main where

import Control.Monad      (void)
import Data.Char          (isDigit)
import Data.List          (intersect)
import Data.Version       (showVersion)
import System.Environment (getArgs)
import System.Exit        (ExitCode(ExitSuccess))
import Control.Concurrent (threadDelay)
import System.Process     (system)

import CountdownLoop      (countdown_loop)
import Pomodoro           (session)

import Paths_Monadoro     (getDataFileName, version)

main :: IO ()
main = monadoro

monadoro :: IO ()
monadoro = getArgs >>= parseArgs

remove :: String -> [String] -> [String]
remove element list = filter (/=element) list

parseArgs :: [String] -> IO ()
parseArgs xs
  | xs `containsEither` ["--help", "-h"]    = putStrLn usage
  | xs `containsEither` ["--version", "-v"] = putStrLn . showVersion $ version
  | xs `containsEither` ["--session", "-s"] = session (getDelayIfNeeded xs)
  | xs `containsEither` ["--man", "-m"] = do
    man_file <- getDataFileName "man/monadoro.1"
    ExitSuccess <- system $ "man " ++ man_file
    return ()
  | otherwise = do
    let filtered_params = filterParams xs
    runPomodoro (getDelayIfNeeded xs) filtered_params

filterParams :: [String] -> [String]
filterParams xs = (remove "-n" . remove "--nodelay") xs

runPomodoro :: IO () -> [String] -> IO ()
runPomodoro delay xs = do
    warnAboutErrorsIfAny invalidIntervals
    runTimer delay validIntervals
    where (validIntervals, invalidIntervals) = checkInput xs

with_delay :: IO()
with_delay = wait 1

-- Mocks a delay for test purposes.
no_delay :: IO()
no_delay = wait 0

getDelayIfNeeded :: [String] -> IO ()
getDelayIfNeeded xs
  | xs `containsEither` ["--nodelay", "-n"] = no_delay
  | otherwise = with_delay

warnAboutErrorsIfAny :: [String] -> IO ()
warnAboutErrorsIfAny [] = return ()
warnAboutErrorsIfAny errors =
  putStrLn $ "Unable to parse as interval: " ++ show errors

{-| checkInput

Returns valid entries (first list) and invalid entries (second list).

>>> checkInput []
([],[])

>>> checkInput ["x"]
([],["x"])

>>> checkInput ["00:00"]
(["00:00"],[])

>>> checkInput ["00:00", "x"]
(["00:00"],["x"])

>>> checkInput ["00:00", "x"]
(["00:00"],["x"])

>>> checkInput ["00:00", "00:01", "x"]
(["00:00","00:01"],["x"])

-}

checkInput :: [String] -> ([String], [String])
checkInput [] = ([], [])
checkInput [x]
  | isValidTimeInterval x = ([x], [])
  | otherwise = ([], [x])
checkInput (x:xs) = (validEntries, invalidEntries)
  where
    validEntries =
      fst (checkInput [x]) ++ fst (checkInput xs)
    invalidEntries =
      snd (checkInput [x]) ++ snd (checkInput xs)

{-| isValidTimeInterval

>>> isValidTimeInterval "x"
False

>>> isValidTimeInterval "00:00"
True

-}

isValidTimeInterval :: String -> Bool
isValidTimeInterval (m1:m2:':':s1:[s2])
  | all isDigit [m1, m2, s1, s2] = True
isValidTimeInterval _ = False

containsEither :: (Eq a) => [a] -> [a] -> Bool
containsEither a = not . null . intersect a

usage :: String
usage = "Usage: monadoro [-h|--help] [-m|--man] [-v|--version] [--session]" ++
  "[INTERVAL [...]]"

milisec_per_second :: Int
milisec_per_second = 10 ^ (6 :: Int)

wait :: Int -> IO()
wait n = threadDelay (n * milisec_per_second)

runTimer :: IO () -> [String] -> IO ()
runTimer delayer []     = void (countdown_loop delayer "25:00")
runTimer delayer [t]    = void (countdown_loop delayer t)
runTimer delayer (t:ts) = parseArgs [t] >> parseArgs ts