diff --git a/app/Main.hs b/app/Main.hs index 37b341e..1d4e599 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -20,6 +20,7 @@ import Control.Monad.Log ( defaultBatchingOptio import Control.Monad.Reader ( runReaderT ) import Control.Monad.Trans ( liftIO ) import Data.FileEmbed ( embedDir ) +import Data.Foldable ( traverse_ ) import Data.IORef ( IORef , readIORef ) @@ -27,8 +28,7 @@ import Data.Time.Format ( defaultTimeLocale , formatTime , iso8601DateFormat ) -import Lens.Micro.Platform ( (<&>) - , set +import Lens.Micro.Platform ( set , view ) import Network.HTTP.Client.TLS ( newTlsManager ) @@ -64,53 +64,52 @@ opts = "Update interval" ] +usage :: IO () +usage = putStrLn $ usageInfo "mat-chalmers [OPTION...]" opts + main :: IO () main = - getArgs - <&> getOpt Permute opts - >>= \case - (_ , _ , _ : _) -> usage - (_ , _ : _, _ ) -> usage - (!confs, _ , _ ) -> do - let config = foldl (flip id) defaultConfig confs - if view cHelp config - then usage - else do - upd <- newMVar () -- putMVar when to update - mgr <- newTlsManager - (viewRef, refreshAction) <- runLoggingT - (runReaderT refresh (ClientContext config mgr)) - print + (reifyConfig . getOpt Permute opts <$> getArgs) + >>= \case + (_ , _ , _ : _) -> usage + (_ , _ : _, _ ) -> usage + (Config{_cHelp=True}, _ , _ ) -> usage + (config, _ , _ ) -> do + upd <- newMVar () -- putMVar when to update + mgr <- newTlsManager + (viewRef, refreshAction) <- runLoggingT + (runReaderT refresh (ClientContext config mgr)) + print + + -- In the list there are three items running concurrently: + -- 1. Timer that sends a signal to the updater when it's time to update + -- 2. Webserver that serves the menus to the user + -- 3. Updater that fetches new data from the restaurants + Async.runConcurrently $ traverse_ Async.Concurrently + [ timer + , webserver config viewRef upd + , updater + ] + where + timer = forever $ tryPutMVar upd () >> threadDelay (view cInterval config) - Async.concurrently_ - (Async.concurrently_ - -- timer - (forever $ tryPutMVar upd () >> threadDelay (view cInterval config)) - -- webserver - (serve config viewRef upd)) - -- updater - (forever - $ withFDHandler defaultBatchingOptions stdout 1.0 80 - $ \logCallback -> - runReaderT (refreshAction upd) (ClientContext config mgr) - `runLoggingT` ( logCallback - . renderWithTimestamp - (formatTime - defaultTimeLocale - (iso8601DateFormat - (Just "%H:%M:%S") - ) - ) - id - )) - where usage = putStrLn $ usageInfo "mat-chalmers [OPTION...]" opts + updater = + forever + $ withFDHandler defaultBatchingOptions stdout 1.0 80 + $ \logCallback -> + runLoggingT + (runReaderT (refreshAction upd) (ClientContext config mgr)) + (logCallback . renderWithTimestamp ( + formatTime defaultTimeLocale (iso8601DateFormat (Just "%H:%M:%S"))) + id + ) -serve +webserver :: Config -> IORef View -- ^ View model -> MVar () -- ^ Update signal -> IO () -serve conf viewRef upd = scotty (view cPort conf) $ do +webserver conf viewRef upd = scotty (view cPort conf) $ do middleware logStdout middleware (static $(embedDir "static")) get "/" ((html . render) =<< liftIO (readIORef viewRef)) diff --git a/src/Config.hs b/src/Config.hs index 3e548cd..316e862 100644 --- a/src/Config.hs +++ b/src/Config.hs @@ -4,6 +4,7 @@ module Config where +import Data.Foldable ( foldl' ) import Lens.Micro.Platform -- | Configuration record @@ -18,3 +19,16 @@ makeLenses ''Config defaultConfig :: Config defaultConfig = Config False 14 (1000000 * 60 * 30) 5007 + +-- | Create a Config we can touch +-- +-- Yes, this function implementation is overly cute. It uses a lens to update +-- the first field of the tuple by using defaultConfig as first argument to +-- every function in the list. The brain needs to turn inside out a couple of +-- times before it makes sense, so if you're in a hurry, look at the types. +-- +-- TODO: Feel free to bikeshed the function name. +reifyConfig + :: ([Config -> Config], [String], [String]) + -> (Config, [String], [String]) +reifyConfig = (& _1 %~ foldl' (flip id) defaultConfig)