From d237eb31ab2d0a3d09bb6c5a6c568e6364e1804b Mon Sep 17 00:00:00 2001 From: Alexander Goussas Date: Sat, 5 Oct 2024 12:19:39 -0500 Subject: [PATCH] feature: Parse configuration file from current directory for additional flags --- app/Main.hs | 138 +++++++++++++++++++++++++++++++++++++++------------ dotenv.cabal | 1 + 2 files changed, 108 insertions(+), 31 deletions(-) diff --git a/app/Main.hs b/app/Main.hs index 3390ec56..c3da374a 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -12,7 +12,9 @@ import Options.Applicative import Paths_dotenv (version) import Configuration.Dotenv (Config (..), defaultConfig, loadFile) +import System.Directory (doesFileExist) import System.Exit (exitWith) +import System.IO (readFile') import System.Process (system) data Options = Options @@ -26,9 +28,42 @@ data Options = Options , args :: [String] } deriving (Show) +data Flags = Flags + { flagDotenvFiles :: [String] + , flagDotenvExampleFiles :: [String] + , flagOverride :: Bool + , flagVerbose :: Bool + , flagDryRun :: Bool + , flagDuplicates :: Bool + } + +defaultFlags :: Flags +defaultFlags = Flags + { flagDotenvFiles = [] + , flagDotenvExampleFiles = [] + , flagOverride = False + , flagVerbose = False + , flagDryRun = False + , flagDuplicates = False + } + +applyFlags :: Options -> Flags -> Options +applyFlags options flags = Options + { dotenvFiles = dotenvFiles options <> flagDotenvFiles flags + , dotenvExampleFiles = dotenvExampleFiles options <> flagDotenvExampleFiles flags + , override = override options || flagOverride flags + , verbose = verbose options || flagVerbose flags + , dryRun = dryRun options || flagDryRun flags + , duplicates = duplicates options || flagDuplicates flags + , program = program options + , args = args options + } + main :: IO () main = do - Options{..} <- execParser opts + options <- execParser $ mkOpts config + flags <- loadFlagsFromConfig + let Options{..} = applyFlags options flags let configDotenv = Config { configExamplePath = dotenvExampleFiles @@ -46,45 +81,86 @@ main = do if configDryRun configDotenv then putStrLn "[INFO]: Dry run mode enabled. Not executing the program." else system (program ++ concatMap (" " ++) args) >>= exitWith - where - opts = info (helper <*> versionOption <*> config) - ( fullDesc - <> progDesc "Runs PROGRAM after loading options from FILE" - <> header "dotenv - loads options from dotenv files" ) - versionOption = - infoOption + +mkOpts :: Parser a -> ParserInfo a +mkOpts p = info (helper <*> versionOption <*> p) + ( fullDesc + <> progDesc "Runs PROGRAM after loading options from FILE" + <> header "dotenv - loads options from dotenv files" ) + where + versionOption = infoOption (showVersion version) (long "version" <> short 'v' <> help "Show version of the program") config :: Parser Options config = Options - <$> many (strOption ( - long "dotenv" - <> short 'f' - <> metavar "DOTENV" - <> help "File to read for environmental variables" )) + <$> dotenvFileOption + <*> exampleFileOption + <*> overloadOption + <*> verboseOption + <*> dryRunOption + <*> noDuplicatesOption + <*> argument str (metavar "PROGRAM") + <*> many (argument str (metavar "ARG")) - <*> many (strOption ( - long "example" - <> short 'x' - <> metavar "DOTENV_EXAMPLE" - <> help "File to read for needed environmental variables" )) +dotenvFileOption :: Parser [String] +dotenvFileOption = many + ( strOption ( + long "dotenv" + <> short 'f' + <> metavar "DOTENV" + <> help "File to read for environmental variables" )) - <*> switch ( long "overload" - <> short 'o' - <> help "Specify this flag to override existing variables" ) +exampleFileOption :: Parser [FilePath] +exampleFileOption = many + ( strOption ( + long "example" + <> short 'x' + <> metavar "DOTENV_EXAMPLE" + <> help "File to read for needed environmental variables" )) - <*> switch ( long "verbose" - <> help "Specify this flag to print out the variables loaded and other useful insights" ) +overloadOption :: Parser Bool +overloadOption = switch + ( long "overload" + <> short 'o' + <> help "Specify this flag to override existing variables" ) - <*> switch ( long "dry-run" - <> help "Specify this flag to print out the variables loaded without executing the program" ) +verboseOption :: Parser Bool +verboseOption = switch + ( long "verbose" + <> help "Specify this flag to print out the variables loaded and other useful insights" ) - <*> flag True False ( long "no-dups" - <> short 'D' - <> help "Specify this flag to forbid duplicate variables" - ) +dryRunOption :: Parser Bool +dryRunOption = switch + ( long "dry-run" + <> help "Specify this flag to print out the variables loaded without executing the program" ) - <*> argument str (metavar "PROGRAM") +noDuplicatesOption :: Parser Bool +noDuplicatesOption = flag True False + ( long "no-dups" + <> short 'D' + <> help "Specify this flag to forbid duplicate variables" ) - <*> many (argument str (metavar "ARG")) +flagsP :: Parser Flags +flagsP = Flags + <$> dotenvFileOption + <*> exampleFileOption + <*> overloadOption + <*> verboseOption + <*> dryRunOption + <*> noDuplicatesOption + +configFile :: FilePath +configFile = "dotenv.config" + +loadFlagsFromConfig :: IO Flags +loadFlagsFromConfig = do + configExists <- doesFileExist configFile + if not configExists then return defaultFlags + else do + arguments <- words <$> readFile' configFile + case execParserPure defaultPrefs (mkOpts flagsP) arguments of + result@(Failure _) -> do + putStrLn "There were errors while parsing the configuration file" + handleParseResult result + result -> handleParseResult result diff --git a/dotenv.cabal b/dotenv.cabal index 8403142b..1232a7e8 100644 --- a/dotenv.cabal +++ b/dotenv.cabal @@ -70,6 +70,7 @@ executable dotenv , megaparsec , process , text + , directory other-modules: Paths_dotenv hs-source-dirs: app