packages feed

hid-examples-0.4: suntimes/ProcessRequest.hs

{-# LANGUAGE RecordWildCards, FlexibleContexts #-}

module ProcessRequest (processMany, processInteractively) where

import System.Locale.Read (TimeLocale, getCurrentLocale)
import Control.Exception.Safe
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Time
import Control.Monad
import Control.Monad.Reader
import Control.Monad.IO.Class

import Control.Concurrent
import Data.Semigroup ((<>)) -- required for GHC 8.2

import App
import Types
import GeoCoordsReq
import SunTimes
import STExcept

parseRequestLine :: T.Text -> Either RequestError (T.Text, When)
parseRequestLine t = parse (split t)
  where
    split t = case T.breakOn "@" t of
                (addr, "") -> ("", addr)
                (day, addr) -> (T.strip day, T.strip $ T.tail addr)
    parse (_, "") = Left EmptyRequest
    parse ("", addr) = Right (addr, Now)
    parse (d, addr) =
      case parseTimeM False defaultTimeLocale "%Y-%m-%d" (T.unpack d) of
        Nothing -> Left (WrongDay d)
        Just d' -> Right (addr, On d')

formatResult :: T.Text -> SunTimes ZonedTime -> TimeLocale -> T.Text
formatResult req SunTimes {..} loc = mconcat [day, " @ ", req,
                                              "\n    ", fmt sunrise,
                                              "\n    ", fmt sunset]
  where
    day = T.pack $ formatTime loc "%x" sunrise
    fmt t = T.pack $ formatTime loc "%X %Z" t

processRequest :: T.Text -> MyApp T.Text
processRequest t = processR (parseRequestLine (T.strip t))
  where
    processR (Left e) = throw (FormatError e)
    processR (Right (addr, day)) = do
      coords <- getCoords addr
      st <- getSunTimes coords day 
      loc <- liftIO getCurrentLocale
             `catchAny` (const $ pure defaultTimeLocale)
      pure $ formatResult addr st loc

processMany :: [T.Text] -> MyApp ()
processMany = mapM_ processRequestWrapper
  where
    processRequestWrapper r =
      unless ("#" `T.isPrefixOf` r)
             $ (processRequest r >>= liftIO .TIO.putStrLn) `catch` handler r
               `finally` delaySec 3
    delaySec sec = liftIO $ threadDelay (sec * 1000000)
    handler :: T.Text -> SunInfoException -> MyApp ()
    handler r e = liftIO $ TIO.putStrLn $ "Error in request '" <> r <> "': "
                         <> T.pack (show e)

processInteractively :: MyApp ()
processInteractively = action `catch` handler
  where
    action = do 
      liftIO $ TIO.putStrLn "Enter your request:"
      req <- liftIO $ TIO.getLine
      res <- processRequest req
      liftIO $ TIO.putStrLn res

    handler :: SunInfoException -> MyApp ()
    handler e@(ServiceAPIError _) = liftIO $ print e
    handler e@(NetworkError _) = liftIO $ print e
    handler e = do
      liftIO $ TIO.putStr
             $ "There was an error while processing your request: "
             <> T.pack (show e) <> "\nDo you want to try again (Y/N)?"
      yesno <- liftIO $ TIO.getLine
      when (yesno `elem` ["y", "Y", "yes"]) processInteractively