{-# LANGUAGE OverloadedStrings #-} -- | Copyright: (c) 2020 berberman -- SPDX-License-Identifier: MIT -- Maintainer: berberman <1793913507@qq.com> -- Stability: experimental -- Portability: portable -- This module defines input patterns used in executables' cli. -- "Options.Applicative" is re-exported. module Distribution.ArchHs.OptionReader ( optFlagReader, optSkippedReader, optExtraCabalReader, optVersionReader, optPackageNameReader, module Options.Applicative, ) where import qualified Data.Map.Strict as Map import Data.Void (Void) import Distribution.ArchHs.Internal.Prelude import Distribution.ArchHs.Utils import Options.Applicative import System.FilePath (takeExtension) import qualified Text.Megaparsec as M import qualified Text.Megaparsec.Char as M readFlag :: [(String, String, Bool)] -> Map.Map PackageName FlagAssignment readFlag [] = Map.empty readFlag list = Map.fromList . fmap (\l -> (mkPackageName . (^. _1) . head $ l, foldr (\(_, f, v) acc -> insertFlagAssignment (mkFlagName f) v acc) (mkFlagAssignment []) l)) . groupBy (\a b -> uncurry (==) (getTwo _1 a b)) $ list -- | Read a set of package name with flag assignments. -- -- >>> f "" -- Right (fromList []) -- >>> f "package_name:flag_name:true" -- Right (fromList [(PackageName "package_name",fromList [(FlagName "flag_name",(1,True))])]) -- >>> f "package_name:flag_name_1:true,package_name:flag_name_2:false" -- Right (fromList [(PackageName "package_name",fromList [(FlagName "flag_name_1",(1,True)),(FlagName "flag_name_2",(1,False))])]) -- >>> f "package_name_1:flag_name_1:false,package_name_2:flag_name_2:true" -- Right (fromList [(PackageName "package_name_1",fromList [(FlagName "flag_name_1",(1,False))]),(PackageName "package_name_2",fromList [(FlagName "flag_name_2",(1,True))])]) -- >>> f "zzz" -- Left "1:4:\n |\n1 | zzz\n | ^\nunexpected end of input\nexpecting ':'\n" optFlagReader :: ReadM (Map.Map PackageName FlagAssignment) optFlagReader = eitherReader ( \s -> case M.parse optFlagParser "" s of Right x -> Right x Left err -> Left $ M.errorBundlePretty err ) optFlagParser :: M.Parsec Void String (Map.Map PackageName FlagAssignment) optFlagParser = readFlag <$> ( do pkg <- M.manyTill M.anySingle $ M.single ':' flg <- M.manyTill M.anySingle $ M.single ':' b <- bool return (pkg, flg, b) ) `M.sepBy` "," where bool = do s <- M.string "true" <|> M.string "false" case s of "true" -> return True "false" -> return False _ -> fail $ "unknown bool: " <> s -- | Read skipped components. -- This never fails, i.e. the return value will be 'Right'. -- >>> f "" -- Right [""] -- >>> f "component_1,component_2" -- Right ["component_1","component_2"] optSkippedReader :: ReadM [String] optSkippedReader = eitherReader $ Right . splitOn "," -- | Read extra cabal files. -- -- >>> f "" -- Left "Unexpected file name: " -- >>> f "a.cabal" -- Right ["a.cabal"] -- >>> f "a.cabal,b.cabal" -- Right ["a.cabal","b.cabal"] -- >>> f "a.what,b.cabal" -- Left "Unexpected file name: a.what" optExtraCabalReader :: ReadM [FilePath] optExtraCabalReader = eitherReader $ \x -> let split = splitOn "," x check = map (\e -> if takeExtension e == ".cabal" then (e, True) else (e, False)) split failed = map fst . filter (not . snd) $ check successful = map fst . filter snd $ check in if failed /= [] then Left ("Unexpected file name: " <> intercalate ", " failed) else Right $ successful -- | 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