From 3c01d2c5e5e14302eb8451d23db5b4ebbfaba4d1 Mon Sep 17 00:00:00 2001 From: Andreas Ekeroot Date: Mon, 25 Sep 2023 13:31:14 +0200 Subject: [PATCH] Flatten the main function This should make it a bit easier to read while hopefully making it easier to change in the future. Use pattern matching to see if the user wants to the see the usage instructions, this allows us to make use of one `case` expression instead of a `case` expression and an `if` expression. Do configuration reification pointfree style while using lenses. --- app/Main.hs | 81 +++++++++++++++++++++++++-------------------------- src/Config.hs | 14 +++++++++ 2 files changed, 54 insertions(+), 41 deletions(-) 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)