packages feed

hdmenu-0.3.0: src/Core/Toml.hs

module Core.Toml
    ( Config(..)
    , getUserConfig  -- :: IO Config
    ) where

import Core.Util

import Data.Text.IO qualified as T
import Toml         qualified

import Toml (Codec (Codec, codecRead), TomlCodec, (.=), (<!>))


-- | Hdmenu's config file.
type Config :: Type
data Config = Config
    { files    :: [ByteString]
    , open     :: ShowBS
    , dmenuExe :: FilePath
    , term     :: ShowBS
    , tty      :: [ByteString]
    , decay    :: Double
    , histPath :: FilePath
      -- ^ Command line option, NOT specifiable in the config file.
    }

-- | Empty config type with all the default values.
defaultCfg :: Config
defaultCfg = Config
    { files    = []
    , dmenuExe = "dmenu"
    , open     = ("xdg-open" <>)
    , term     = ("xterm"    <>)
    , tty      = []
    , decay    = 1
    , histPath = ""
    }

-- | Parse the config file.
configCodec :: TomlCodec Config
configCodec = Config
    <$> defFiles (Toml.arrayOf Toml._ByteString "files")           .= files
    <*> toDL (defOpen (Toml.byteString "open"))                    .= open
    <*> defDmenu (Toml.string "executable")                        .= dmenuExe
    <*> toDL (defTerm (Toml.byteString "terminal"))                .= term
    <*> defTtyProgs (Toml.arrayOf Toml._ByteString "tty-programs") .= tty
    <*> defDecay (Toml.double "decay")                             .= decay
    <*> pure ""
  where
    -- Parse an option or—in case it's missing—return a default value.
    tomlWithDefault :: a -> TomlCodec a -> TomlCodec a
    tomlWithDefault def codec@Codec{ codecRead } =
        codec { codecRead = codecRead <!> const (pure def) }

    toDL :: TomlCodec ByteString -> TomlCodec ShowBS
        = Toml.dimap ($ "") (<>)

    defFiles    = tomlWithDefault (files    defaultCfg)
    defOpen     = tomlWithDefault (open     defaultCfg "")
    defDmenu    = tomlWithDefault (dmenuExe defaultCfg)
    defTerm     = tomlWithDefault (term     defaultCfg "")
    defTtyProgs = tomlWithDefault (tty      defaultCfg)
    defDecay    = tomlWithDefault (decay    defaultCfg)

-- | Try to find a user config and, if it exists, parse it.
getUserConfig :: IO Config
getUserConfig = do
    -- Default path where to look for the config file:
    --          ~/.config/hdmenu/hdmenu.toml
    cfgFile <- hdmenuPath <</>> "hdmenu.toml"
    ifM (doesFileExist cfgFile)
        (fromRight defaultCfg . Toml.decode configCodec <$> T.readFile cfgFile)
        (pure defaultCfg)