--
-- Looked at the several existing config-file parsing libs
-- they were all overkill
-- |This module does the strict minimum needed parsing INI-style
-- config files
--

{-# LANGUAGE ScopedTypeVariables #-}

module Rascal.Conf where

import Data.Map            (Map, union, fromList)
import Data.Char           (isSpace)
import Data.List           (dropWhileEnd)
import Control.Exception   (handle)
import Control.Monad       (liftM)

import System.Directory    (getHomeDirectory)
import System.FilePath     ((</>))

type Key   = String
type Value = String
type Conf  = Map Key Value

-- |Parses a string into a Map String String
parseConfig :: String -> Conf
parseConfig = fromList . map getKeyValue . filter configLine . lines

-- |keep only lines that will lead to some configuration element
-- stripping out comments, empty lines, etc.
configLine :: String -> Bool
configLine l =
   not (null l) && head l /= '#' && (':' `elem` l || '=' `elem` l)

-- |Turns a line into a key, value pair
getKeyValue :: String -> (Key, Value)
getKeyValue line =
   let (key, _:value) = break (`elem` ":=") line
   in (stripWhite key, stripWhite value)

-- |drop leading and ending white space
stripWhite :: String -> String
stripWhite =
   dropWhile isSpace . dropWhileEnd isSpace

-- |search for fileName in the user's home directory and combine it with
-- default options to provide a Conf map
getUserConfig :: String -> [(String, String)] -> IO Conf
getUserConfig fileName defaultOptions = do
   home <- getHomeDirectory
   let userConfFile = home </> fileName
       defaultConf = fromList defaultOptions
   handle (\(_ :: IOError) -> return defaultConf) $
      -- union will take left over right if key is defined twice
      liftM ((`union` defaultConf) . parseConfig) (readFile userConfFile)