{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
module Distribution.Types.PkgconfigVersionRange (
PkgconfigVersionRange (..),
anyPkgconfigVersion,
isAnyPkgconfigVersion,
withinPkgconfigVersionRange,
versionToPkgconfigVersion,
versionRangeToPkgconfigVersionRange,
) where
import Distribution.Compat.Prelude
import Prelude ()
import Distribution.CabalSpecVersion
import Distribution.Parsec
import Distribution.Pretty
import Distribution.Types.PkgconfigVersion
import Distribution.Types.Version
import Distribution.Types.VersionRange
import qualified Data.ByteString.Char8 as BS8
import qualified Distribution.Compat.CharParsing as P
import qualified Text.PrettyPrint as PP
data PkgconfigVersionRange
= PcAnyVersion
| PcThisVersion PkgconfigVersion
| PcLaterVersion PkgconfigVersion
| PcEarlierVersion PkgconfigVersion
| PcOrLaterVersion PkgconfigVersion
| PcOrEarlierVersion PkgconfigVersion
| PcUnionVersionRanges PkgconfigVersionRange PkgconfigVersionRange
| PcIntersectVersionRanges PkgconfigVersionRange PkgconfigVersionRange
deriving (Generic, Read, Show, Eq, Typeable, Data)
instance Binary PkgconfigVersionRange
instance NFData PkgconfigVersionRange where rnf = genericRnf
instance Pretty PkgconfigVersionRange where
pretty = pp 0 where
pp :: Int -> PkgconfigVersionRange -> PP.Doc
pp _ PcAnyVersion = PP.text "-any"
pp _ (PcThisVersion v) = PP.text "==" <<>> pretty v
pp _ (PcLaterVersion v) = PP.text ">" <<>> pretty v
pp _ (PcEarlierVersion v) = PP.text "<" <<>> pretty v
pp _ (PcOrLaterVersion v) = PP.text ">=" <<>> pretty v
pp _ (PcOrEarlierVersion v) = PP.text "<=" <<>> pretty v
pp d (PcUnionVersionRanges v u) = parens (d >= 1) $
pp 1 v PP.<+> PP.text "||" PP.<+> pp 0 u
pp d (PcIntersectVersionRanges v u) = parens (d >= 2) $
pp 2 v PP.<+> PP.text "&&" PP.<+> pp 1 u
parens True = PP.parens
parens False = id
instance Parsec PkgconfigVersionRange where
parsec = do
csv <- askCabalSpecVersion
if csv >= CabalSpecV3_0
then pkgconfigParser
else versionRangeToPkgconfigVersionRange <$> versionRangeParser P.integral
pkgconfigParser :: CabalParsing m => m PkgconfigVersionRange
pkgconfigParser = P.spaces >> expr where
expr = do
ts <- term `P.sepByNonEmpty` (P.string "||" >> P.spaces)
return $ foldr1 PcUnionVersionRanges ts
term = do
fs <- factor `P.sepByNonEmpty` (P.string "&&" >> P.spaces)
return $ foldr1 PcIntersectVersionRanges fs
factor = parens expr <|> prim
prim = do
op <- P.munch1 (`elem` "<>=^-") P.<?> "operator"
case op of
"-" -> anyPkgconfigVersion <$ (P.string "any" *> P.spaces)
"==" -> afterOp PcThisVersion
">" -> afterOp PcLaterVersion
"<" -> afterOp PcEarlierVersion
">=" -> afterOp PcOrLaterVersion
"<=" -> afterOp PcOrEarlierVersion
_ -> P.unexpected $ "Unknown version operator " ++ show op
afterOp f = do
P.spaces
v <- parsec
P.spaces
return (f v)
parens = P.between
((P.char '(' P.<?> "opening paren") >> P.spaces)
(P.char ')' >> P.spaces)
anyPkgconfigVersion :: PkgconfigVersionRange
anyPkgconfigVersion = PcAnyVersion
isAnyPkgconfigVersion :: PkgconfigVersionRange -> Bool
isAnyPkgconfigVersion = (== PcAnyVersion)
withinPkgconfigVersionRange :: PkgconfigVersion -> PkgconfigVersionRange -> Bool
withinPkgconfigVersionRange v = go where
go PcAnyVersion = True
go (PcThisVersion u) = v == u
go (PcLaterVersion u) = v > u
go (PcEarlierVersion u) = v < u
go (PcOrLaterVersion u) = v >= u
go (PcOrEarlierVersion u) = v <= u
go (PcUnionVersionRanges a b) = go a || go b
go (PcIntersectVersionRanges a b) = go a && go b
versionToPkgconfigVersion :: Version -> PkgconfigVersion
versionToPkgconfigVersion = PkgconfigVersion . BS8.pack . prettyShow
versionRangeToPkgconfigVersionRange :: VersionRange -> PkgconfigVersionRange
versionRangeToPkgconfigVersionRange = foldVersionRange
anyPkgconfigVersion
(PcThisVersion . versionToPkgconfigVersion)
(PcLaterVersion . versionToPkgconfigVersion)
(PcEarlierVersion . versionToPkgconfigVersion)
PcUnionVersionRanges
PcIntersectVersionRanges