-- | -- Module : Configuration.Dotenv.Parse -- Copyright : © 2015–2016 Stack Builders Inc. -- License : MIT -- -- Maintainer : Stack Builders -- Stability : experimental -- Portability : portable -- -- Parser for files in dotenv format. These files generally consist of lines -- with the form key=value. Comments and blank lines are also supported. More -- information on the dotenv format can be found in the project README and the -- test suite. module Configuration.Dotenv.Parse (configParser) where import Text.Megaparsec ((), anyChar, char, eof, manyTill, try) import Text.Megaparsec.String (Parser) import Text.Megaparsec.Char (digitChar, letterChar, eol, noneOf, oneOf) import Control.Applicative import Prelude import Data.Maybe (catMaybes) import Control.Monad (liftM2) -- | Returns a parser for a Dotenv configuration file. Accepts key -- and value arguments separated by "=". Comments are allowed on -- lines by themselves and on blank lines. configParser :: Parser [(String, String)] configParser = catMaybes <$> many envLine <* eof envLine :: Parser (Maybe (String, String)) envLine = (comment <|> blankLine) *> return Nothing <|> Just <$> optionLine blankLine :: Parser String blankLine = many verticalSpace <* eol "blank line" optionLine :: Parser (String, String) optionLine = liftM2 (,) (many verticalSpace *> variableName <* variableValueSeparator) value -- | Variables must start with a letter or underscore, and may contain -- letters, digits or '_' character after the first character. variableName :: Parser String variableName = liftM2 (:) (letterChar <|> char '_') (many (letterChar <|> char '_' <|> digitChar unwords [ "valid non-leading shell variable character (alphanumeric," , "digit or underscore)" ])) unwords [ "shell variable name (letter or underscore followed" , "by alphanumeric characters or underscores)" ] value :: Parser String value = quotedValue <|> unquotedValue "variable value" quotedValue :: Parser String quotedValue = (quotedWith '\'' <|> quotedWith '\"') <* (comment *> return () <|> many verticalSpace *> endOfLineOrInput) "variable value surrounded with single or double quotes" unquotedValue :: Parser String unquotedValue = manyTill anyChar (comment <|> many verticalSpace <* endOfLineOrInput) -- | Based on a commented-string parser in: -- http://hub.darcs.net/navilan/XMonadTasks/raw/Data/Config/Lexer.hs quotedWith :: Char -> Parser String quotedWith c = char c *> many chr <* (char c "closing quote character") where chr = esc <|> noneOf [c] esc = escape *> char c "escape character" comment :: Parser String comment = try (many verticalSpace *> char '#') *> manyTill anyChar endOfLineOrInput "comment" endOfLineOrInput :: Parser () endOfLineOrInput = eol *> return () <|> eof variableValueSeparator :: Parser () variableValueSeparator = many verticalSpace *> (char '=' "variable-value separator character (=)") *> many verticalSpace *> return () escape :: Parser Char escape = char '\\' verticalSpace :: Parser Char verticalSpace = oneOf " \t"