Configger-0.1: src/Data/Configger.hs
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 two configurations
merge :: Config -> Config -> Config
merge a b = foldr mergeSection b a
-- Merge a section into a configuration
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]
-- Add a setting
set :: String -> String -> String -> Config -> Config
set s k v conf = mergeSection (s, [(k, v)]) conf
-- Get the first value of a setting
get :: String -> String -> Config -> Maybe String
get s k conf = lookup s conf >>= lookup k
-- Get all values of a setting
values :: String -> String -> Config -> [String]
values s k conf = let
vars = fromMaybe [] (lookup s conf)
ours = filter ((k ==) . fst) vars
in map snd ours
-- Get the items in a section of a configuration
items :: String -> Config -> [(String, String)]
items name conf = fromMaybe [] (lookup name conf)
-- Load a configuration from a file
load :: (Errorable m, MonadIO m) => FilePath -> String -> m Config
load defaults filename = do
text <- liftIO $ readFile filename
dangerize $ parse (file defaults) filename text
-- Load a configuration from a string
-- (Won't be able to throw as nice error messages)
from :: (Errorable m) => String -> String -> m Config
from str defaults = dangerize $ parse (file defaults) "(unknown)" str
-- | Parsers
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 ()