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 ()