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.VersionInterval
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 Structured 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 csv
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 isOpChar 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
isOpChar '<' = True
isOpChar '=' = True
isOpChar '>' = True
isOpChar '^' = True
isOpChar '-' = True
isOpChar _ = False
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 vr
| isAnyVersion vr
= PcAnyVersion
| otherwise
= case asVersionIntervals vr of
[] -> PcEarlierVersion (PkgconfigVersion (BS8.pack "0"))
(i:is) -> foldl (\r j -> PcUnionVersionRanges r (conv j)) (conv i) is
where
conv (LowerBound v b, NoUpperBound) = convL v b
conv (LowerBound v b, UpperBound u c) = PcIntersectVersionRanges (convL v b) (convU u c)
convL v ExclusiveBound = PcLaterVersion (versionToPkgconfigVersion v)
convL v InclusiveBound = PcOrLaterVersion (versionToPkgconfigVersion v)
convU v ExclusiveBound = PcEarlierVersion (versionToPkgconfigVersion v)
convU v InclusiveBound = PcOrEarlierVersion (versionToPkgconfigVersion v)