module Main where
+import Configuration (ServerConfiguration(..), defaultConfiguration, readConfiguration)
import Control.Monad ((<=<))
-import Data.Maybe (fromMaybe, listToMaybe)
-import Network.Wai.Handler.Warp (Port(..), run)
+import Control.Monad.IO.Class (liftIO)
+import Data.Either (either)
+import Data.Maybe (maybe, listToMaybe)
+import Network.Wai.Handler.Warp (run)
import Server
+import ServerMonad (ConfigMonad(..), ServerMonad(..))
import System.Environment (getArgs)
import Text.Read (readMaybe)
main :: IO ()
-main = getPort >>= flip run app
+main = runConfigMonad getConfiguration >>= flip run app . configPort
-getPort :: IO Port
-getPort = getArgs >>= pure . fromMaybe 5000 . (readMaybe <=< listToMaybe)
+getConfiguration :: ConfigMonad ServerConfiguration
+getConfiguration = do
+ configFilePath <- liftIO getArgs >>= pure . (readMaybe <=< listToMaybe)
+ maybe (pure $ Right defaultConfiguration) readConfiguration configFilePath >>= pure . either error id
- FlexibleContexts
- FlexibleInstances
- GADTs
+- GeneralizedNewtypeDeriving
- KindSignatures
- MultiParamTypeClasses
- OverloadedStrings
--- /dev/null
+module Configuration where
+
+import Control.Monad ((<=<))
+import Data.Aeson (FromJSON(..), eitherDecodeStrict')
+import GHC.Generics (Generic(..))
+import Network.Wai.Handler.Warp (Port(..))
+import qualified Data.ByteString as B
+
+class (Monad m) => MonadReadConfig m where
+ readConfigFile :: FilePath -> m B.ByteString
+
+data ServerConfiguration = ServerConfiguration { configPort :: !Port
+ , configShowExceptions :: !Bool
+ } deriving (Generic)
+
+instance FromJSON ServerConfiguration
+
+defaultConfiguration :: ServerConfiguration
+defaultConfiguration = ServerConfiguration { configPort = 5000
+ , configShowExceptions = False
+ }
+
+readConfiguration :: (MonadReadConfig m) => FilePath -> m (Either String ServerConfiguration)
+readConfiguration = pure . eitherDecodeStrict' <=< readConfigFile
--- /dev/null
+module ServerMonad where
+
+import Configuration (ServerConfiguration(..), MonadReadConfig(..))
+import Control.Monad.IO.Class (MonadIO(..), liftIO)
+import Control.Monad.Reader (MonadReader(..), ReaderT(..))
+import qualified Data.ByteString as B
+
+newtype ConfigMonad a = ConfigMonad { runConfigMonad :: IO a
+ } deriving (Functor, Applicative, Monad, MonadIO)
+
+newtype ServerMonad a = ServerMonad { runServerMonad :: ReaderT ServerConfiguration IO a
+ } deriving (Functor, Applicative, Monad, MonadIO, MonadReader ServerConfiguration)
+
+instance MonadReadConfig ConfigMonad where
+ readConfigFile = liftIO . B.readFile