arch-hs-0.14.1: src/Distribution/ArchHs/Options.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Copyright: (c) 2020-2021 berberman
-- SPDX-License-Identifier: MIT
-- Maintainer: berberman <berberman@yandex.com>
-- Stability: experimental
-- Portability: portable
-- This module contains CLI parsers used in executables.
-- "Options.Applicative.Simple" is re-exported.
module Distribution.ArchHs.Options
( -- * Load Extra DB
ExtraDBOptions (..),
extraDBOptionsParser,
-- * Load files DB
FilesDBOptions (..),
filesDBOptionsParser,
pacmanDBOptionsParser,
-- * Load Hackage DB
HackageDBOptions (..),
hackageDBOptionsParser,
-- * Parse flags
optFlagAssignmentParser,
optFlagReader,
-- * Readers
optPackageNameReader,
optVersionReader,
module Options.Applicative.Simple,
)
where
import qualified Data.Map.Strict as Map
import Distribution.ArchHs.ExtraDB
import Distribution.ArchHs.FilesDB
import Distribution.ArchHs.Hackage
import Distribution.ArchHs.Internal.Prelude
import Distribution.ArchHs.PP
import Distribution.ArchHs.Types
import Options.Applicative.Simple
-----------------------------------------------------------------------------
-- | Parsed options for loading [extra]
newtype ExtraDBOptions = ExtraDBOptions
{ loadExtraDBFromOptions :: IO ExtraDB
}
-- | CLI options parser of 'ExtraDBOptions'
--
-- When alpm is enabled, it also reads a flag @alpm@.
extraDBOptionsParser :: Parser ExtraDBOptions
extraDBOptionsParser =
mkExtraDBOptions <$> alpmOptionsParser <*> extraDBPathParser
extraDBPathParser :: Parser FilePath
extraDBPathParser =
strOption $
long "extra"
<> metavar "PATH"
<> short 'c'
<> help "Path to extra.db"
<> showDefault
<> value defaultExtraDBPath
mkExtraDBOptions :: Bool -> FilePath -> ExtraDBOptions
mkExtraDBOptions useAlpm path =
ExtraDBOptions
( do
let src = if useAlpm then "libalpm" else path
printInfo $ "Loading extra.db from" <+> pretty src
#ifdef ALPM
if useAlpm
then loadExtraDBFFI
else loadExtraDB path
#else
loadExtraDB path
#endif
)
-----------------------------------------------------------------------------
-- | Parsed options for loading 'FilesDB'
newtype FilesDBOptions = FilesDBOptions
{ loadFilesDBFromOptions :: DBKind -> IO FilesDB
}
-- | CLI options parser of 'ExtraDBOptions'
--
-- When alpm is enabled, it also reads a flag @alpm@.
filesDBOptionsParser :: Parser FilesDBOptions
filesDBOptionsParser =
mkFilesDBOptions <$> alpmOptionsParser <*> filesDBPathParser
filesDBPathParser :: Parser FilePath
filesDBPathParser =
strOption $
long "files"
<> metavar "PATH"
<> short 'f'
<> help
"Path of dir that includes core.files, extra.files and extra.files"
<> showDefault
<> value defaultFilesDBDir
mkFilesDBOptions :: Bool -> FilePath -> FilesDBOptions
mkFilesDBOptions useAlpm path =
FilesDBOptions
( \db ->
do
let src = if useAlpm then "libalpm" else path
printInfo $
"Loading" <+> ppDBKind db <+> "files from" <+> pretty src
#ifdef ALPM
if useAlpm then loadFilesDBFFI db else loadFilesDB db path
#else
loadFilesDB db path
#endif
)
-- | CLI options parser for commands that need both pacman database loaders.
--
-- When alpm is enabled, it reads @alpm@ once and applies it to both loaders.
pacmanDBOptionsParser :: Parser (ExtraDBOptions, FilesDBOptions)
pacmanDBOptionsParser =
mkPacmanDBOptions
<$> alpmOptionsParser
<*> extraDBPathParser
<*> filesDBPathParser
where
mkPacmanDBOptions useAlpm extraPath filesPath =
(mkExtraDBOptions useAlpm extraPath, mkFilesDBOptions useAlpm filesPath)
alpmOptionsParser :: Parser Bool
#ifndef ALPM
alpmOptionsParser = pure False
#else
alpmOptionsParser =
switch $
long "alpm"
<> help "Use libalpm to load pacman databases"
#endif
-----------------------------------------------------------------------------
-- | Parsed options for loading 'HackageDB'
data HackageDBOptions = HackageDBOptions
{ loadHackageDBFromOptions :: IO HackageDB,
loadRawHackageDBFromOptions :: IO RawHackageDB,
loadHackageDBsFromOptions :: IO (HackageDB, RawHackageDB)
}
-- | CLI options parser that reads a string option @hackage@.
hackageDBOptionsParser :: Parser HackageDBOptions
hackageDBOptionsParser =
mkHackageDBOptions
<$> strOption
( long "hackage"
<> metavar "PATH"
<> short 'h'
<> help "Path to hackage index tarball"
<> showDefault
<> value ""
)
where
resolvePath s = if null s then lookupHackagePath else pure s
withHackagePath s load = do
hackagePath <- resolvePath s
printInfo $ "Loading hackage from" <+> pretty hackagePath
load hackagePath
mkHackageDBOptions s =
HackageDBOptions
{ loadHackageDBFromOptions = withHackagePath s loadHackageDB,
loadRawHackageDBFromOptions = withHackagePath s loadRawHackageDB,
loadHackageDBsFromOptions = withHackagePath s loadHackageDBs
}
-----------------------------------------------------------------------------
-- | Read a flag assignment like @package_name:flag_name:true|false@
optFlagReader :: ReadM (String, String, Bool)
optFlagReader = eitherReader $ \s -> case splitOn ":" s of
[name, fname, fvalue] -> case fvalue of
"true" -> Right (name, fname, True)
"false" -> Right (name, fname, False)
_ -> Left "Unknown boolean value, it should be 'true' or 'false'"
_ -> Left "Failed to parse flag assignment"
-- | CLI options parser of flag assignments
optFlagAssignmentParser :: Parser (Map.Map PackageName FlagAssignment)
optFlagAssignmentParser =
fmap toFlagAssignment <$> many $
option optFlagReader $
long "flag"
<> metavar "package_name:flag_name:true|false"
<> short 'f'
<> help "A sinlge flag assignment for a package - e.g. inline-c:gsl-example:true"
toFlagAssignment :: [(String, String, Bool)] -> Map.Map PackageName FlagAssignment
toFlagAssignment xs =
Map.map toAssignment $
foldr (\(name, fname, fvalue) acc -> Map.insertWith (<>) (mkPackageName name) [(mkFlagName fname, fvalue)] acc) Map.empty xs
where
toAssignment = foldr (\(fname, fvalue) acc -> insertFlagAssignment fname fvalue acc) (mkFlagAssignment [])
-----------------------------------------------------------------------------
-- | Read a 'Version'
-- This function calls 'simpleParsec'.
optVersionReader :: ReadM Version
optVersionReader =
eitherReader
( \s -> case simpleParsec s of
Just v -> Right v
_ -> Left $ "Failed to parse version: " <> s
)
-- | Read a 'PackageName'
-- This function never fails, because it just wraps the input string with 'mkPackageName'.
optPackageNameReader :: ReadM PackageName
optPackageNameReader = eitherReader $ Right . mkPackageName