packages feed

neuron-0.4.0.0: src/app/Neuron/Config.hs

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Neuron.Config
  ( Config (..),
    configFile,
    getConfig,
    BaseUrlError (..),
    getMarkdownExtensions,
  )
where

import Control.Monad.Except
import Data.FileEmbed (embedFile)
import Development.Shake (Action, readFile')
import Dhall (FromDhall)
import qualified Dhall
import Dhall.TH
import Relude
import qualified Rib
import System.Directory
import System.FilePath
import qualified Text.MMark as MMark
import qualified Text.MMark.Extension.Common as Ext
import Text.MMark.Extension.SetTableClass (setTableClass)

-- | Config type for @neuron.dhall@
--
-- See <https://neuron.zettel.page/2011701.html guide> for description of the fields.
makeHaskellTypes
  [ SingleConstructor "Config" "Config" "./src-dhall/Config/Type.dhall"
  ]

deriving instance Generic Config

deriving instance FromDhall Config

data BaseUrlError
  = BaseUrlNotAbsolute
  deriving (Eq, Show)

instance Exception BaseUrlError

defaultConfig :: ByteString
defaultConfig = $(embedFile "./src-dhall/Config/Default.dhall")

configFile :: FilePath
configFile = "neuron.dhall"

-- | Read the optional @neuron.dhall@ config file from the zettelksaten
getConfig :: Action Config
getConfig = do
  inputDir <- Rib.ribInputDir
  let configPath = inputDir </> configFile
  configVal :: Text <- liftIO (doesFileExist configPath) >>= \case
    True -> do
      userConfig <- fmap toText $ readFile' configPath
      -- Dhall's combine operator (`//`) allows us to merge two records,
      -- effectively merging the record with defaults with the user record.
      pure $ decodeUtf8 defaultConfig <> " // " <> userConfig
    False ->
      pure $ decodeUtf8 @Text defaultConfig
  parseConfig configVal

parseConfig :: MonadIO m => Text -> m Config
parseConfig s =
  liftIO $ Dhall.input Dhall.auto s

getMarkdownExtensions :: Config -> [MMark.Extension]
getMarkdownExtensions Config {..} =
  defaultExts
    <> bool [] [Ext.mathJax (Just '$')] mathJaxSupport
  where
    defaultExts :: [MMark.Extension]
    defaultExts =
      [ Ext.fontAwesome,
        Ext.footnotes,
        Ext.kbd,
        Ext.linkTarget,
        Ext.punctuationPrettifier,
        Ext.skylighting,
        setTableClass "ui celled table"
      ]