module Data.Configger
( Config
, load
, from
, merge
, mergeSection
, items
, set
, get
, values ) where
import Control.Dangerous
import Control.Monad.Trans
import Data.Maybe
import Data.String.Utils
import Text.ParserCombinators.Parsec
type Config = [(String, [(String, String)])]
merge :: Config -> Config -> Config
merge a b = foldr mergeSection b a
mergeSection :: (String, [(String, String)]) -> Config -> Config
mergeSection (a, as) ((b, bs):rest) | a == b = (a, as ++ bs) : rest
mergeSection a (b:bs) = b : mergeSection a bs
mergeSection a [] = [a]
set :: String -> String -> String -> Config -> Config
set s k v conf = mergeSection (s, [(k, v)]) conf
get :: String -> String -> Config -> Maybe String
get s k conf = lookup s conf >>= lookup k
values :: String -> String -> Config -> [String]
values s k conf = let
vars = fromMaybe [] (lookup s conf)
ours = filter ((k ==) . fst) vars
in map snd ours
items :: String -> Config -> [(String, String)]
items name conf = fromMaybe [] (lookup name conf)
load :: (Errorable m, MonadIO m) => FilePath -> String -> m Config
load defaults filename = do
text <- liftIO $ readFile filename
dangerize $ parse (file defaults) filename text
from :: (Errorable m) => String -> String -> m Config
from str defaults = dangerize $ parse (file defaults) "(unknown)" str
file :: String -> GenParser Char st Config
file defaults = do
free <- sepEndBy setting (many1 eol)
named <- sepEndBy section (many1 eol)
return $ mergeSection (defaults, free) named
section :: GenParser Char st (String, [(String, String)])
section = do
w >> char '[' >> w
name <- word
w >> char ']' >> many1 eol >> return ()
vars <- sepEndBy setting eol
return (name, vars)
setting :: GenParser Char st (String, String)
setting = do
var <- w >> word
w >> oneOf ":=" >> w
val <- w >> word
return (var, val)
word :: GenParser Char st String
word = do
str <- many1 (noneOf ":=[]\n")
return $ strip str
w :: GenParser Char st ()
w = many (oneOf " \t") >> return ()
eol :: GenParser Char st ()
eol = w >> char '\n' >> return ()