{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Distribution.Types.Flag (
Flag(..),
emptyFlag,
FlagName,
mkFlagName,
unFlagName,
FlagAssignment,
mkFlagAssignment,
unFlagAssignment,
lookupFlagAssignment,
insertFlagAssignment,
diffFlagAssignment,
findDuplicateFlagAssignments,
nullFlagAssignment,
showFlagValue,
dispFlagAssignment,
parsecFlagAssignment,
) where
import Prelude ()
import Distribution.Compat.Prelude
import Distribution.Utils.ShortText
import Distribution.Utils.Generic (lowercase)
import Distribution.Parsec
import Distribution.Pretty
import qualified Data.Map as Map
import qualified Text.PrettyPrint as Disp
import qualified Distribution.Compat.CharParsing as P
data Flag = MkFlag
{ flagName :: FlagName
, flagDescription :: String
, flagDefault :: Bool
, flagManual :: Bool
}
deriving (Show, Eq, Typeable, Data, Generic)
instance Binary Flag
instance Structured Flag
instance NFData Flag where rnf = genericRnf
emptyFlag :: FlagName -> Flag
emptyFlag name = MkFlag
{ flagName = name
, flagDescription = ""
, flagDefault = True
, flagManual = False
}
newtype FlagName = FlagName ShortText
deriving (Eq, Generic, Ord, Show, Read, Typeable, Data, NFData)
mkFlagName :: String -> FlagName
mkFlagName = FlagName . toShortText
instance IsString FlagName where
fromString = mkFlagName
unFlagName :: FlagName -> String
unFlagName (FlagName s) = fromShortText s
instance Binary FlagName
instance Structured FlagName
instance Pretty FlagName where
pretty = Disp.text . unFlagName
instance Parsec FlagName where
parsec = mkFlagName . lowercase <$> parsec'
where
parsec' = (:) <$> lead <*> rest
lead = P.satisfy (\c -> isAlphaNum c || c == '_')
rest = P.munch (\c -> isAlphaNum c || c == '_' || c == '-')
newtype FlagAssignment
= FlagAssignment { getFlagAssignment :: Map.Map FlagName (Int, Bool) }
deriving (Binary, Generic, NFData, Typeable)
instance Structured FlagAssignment
instance Eq FlagAssignment where
(==) (FlagAssignment m1) (FlagAssignment m2)
= fmap snd m1 == fmap snd m2
instance Ord FlagAssignment where
compare (FlagAssignment m1) (FlagAssignment m2)
= fmap snd m1 `compare` fmap snd m2
combineFlagValues :: (Int, Bool) -> (Int, Bool) -> (Int, Bool)
combineFlagValues (c1, _) (c2, b2) = (c1 + c2, b2)
instance Semigroup FlagAssignment where
(<>) (FlagAssignment m1) (FlagAssignment m2)
= FlagAssignment (Map.unionWith combineFlagValues m1 m2)
instance Monoid FlagAssignment where
mempty = FlagAssignment Map.empty
mappend = (<>)
mkFlagAssignment :: [(FlagName, Bool)] -> FlagAssignment
mkFlagAssignment =
FlagAssignment .
Map.fromListWith (flip combineFlagValues) . fmap (fmap (\b -> (1, b)))
unFlagAssignment :: FlagAssignment -> [(FlagName, Bool)]
unFlagAssignment = fmap (fmap snd) . Map.toList . getFlagAssignment
nullFlagAssignment :: FlagAssignment -> Bool
nullFlagAssignment = Map.null . getFlagAssignment
lookupFlagAssignment :: FlagName -> FlagAssignment -> Maybe Bool
lookupFlagAssignment fn = fmap snd . Map.lookup fn . getFlagAssignment
insertFlagAssignment :: FlagName -> Bool -> FlagAssignment -> FlagAssignment
insertFlagAssignment flag val =
FlagAssignment .
Map.insertWith (flip combineFlagValues) flag (1, val) . getFlagAssignment
diffFlagAssignment :: FlagAssignment -> FlagAssignment -> FlagAssignment
diffFlagAssignment fa1 fa2 = FlagAssignment
(Map.difference (getFlagAssignment fa1) (getFlagAssignment fa2))
findDuplicateFlagAssignments :: FlagAssignment -> [FlagName]
findDuplicateFlagAssignments =
Map.keys . Map.filter ((> 1) . fst) . getFlagAssignment
instance Read FlagAssignment where
readsPrec p s = [ (FlagAssignment x, rest) | (x,rest) <- readsPrec p s ]
instance Show FlagAssignment where
showsPrec p (FlagAssignment xs) = showsPrec p xs
showFlagValue :: (FlagName, Bool) -> String
showFlagValue (f, True) = '+' : unFlagName f
showFlagValue (f, False) = '-' : unFlagName f
dispFlagAssignment :: FlagAssignment -> Disp.Doc
dispFlagAssignment = Disp.hsep . map (Disp.text . showFlagValue) . unFlagAssignment
parsecFlagAssignment :: CabalParsing m => m FlagAssignment
parsecFlagAssignment = mkFlagAssignment <$>
P.sepBy (onFlag <|> offFlag) P.skipSpaces1
where
onFlag = do
_ <- P.optional (P.char '+')
f <- parsec
return (f, True)
offFlag = do
_ <- P.char '-'
f <- parsec
return (f, False)