module Distribution.Types.VersionRange (
    -- * Version ranges
    VersionRange,

    -- ** Constructing
    anyVersion, noVersion,
    thisVersion, notThisVersion,
    laterVersion, earlierVersion,
    orLaterVersion, orEarlierVersion,
    unionVersionRanges, intersectVersionRanges,
    withinVersion,
    majorBoundVersion,

    -- ** Inspection
    --
    -- See "Distribution.Version" for more utilities.
    withinRange,
    foldVersionRange,
    normaliseVersionRange,
    stripParensVersionRange,
    hasUpperBound,
    hasLowerBound,

    -- ** Cata & ana
    VersionRangeF (..),
    cataVersionRange,
    anaVersionRange,
    hyloVersionRange,
    projectVersionRange,
    embedVersionRange,

    -- ** Utilities
    wildcardUpperBound,
    majorUpperBound,
    isWildcardRange,
    versionRangeParser,
    ) where

import Distribution.Compat.Prelude
import Distribution.Types.Version
import Distribution.Types.VersionRange.Internal
import Prelude ()

-- | Fold over the basic syntactic structure of a 'VersionRange'.
--
-- This provides a syntactic view of the expression defining the version range.
-- The syntactic sugar @\">= v\"@, @\"<= v\"@ and @\"== v.*\"@ is presented
-- in terms of the other basic syntax.
--
-- For a semantic view use 'asVersionIntervals'.
--
foldVersionRange :: a                         -- ^ @\"-any\"@ version
                 -> (Version -> a)            -- ^ @\"== v\"@
                 -> (Version -> a)            -- ^ @\"> v\"@
                 -> (Version -> a)            -- ^ @\"< v\"@
                 -> (a -> a -> a)             -- ^ @\"_ || _\"@ union
                 -> (a -> a -> a)             -- ^ @\"_ && _\"@ intersection
                 -> VersionRange -> a
foldVersionRange :: a
-> (Version -> a)
-> (Version -> a)
-> (Version -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> VersionRange
-> a
foldVersionRange a
anyv Version -> a
this Version -> a
later Version -> a
earlier a -> a -> a
union a -> a -> a
intersect = VersionRange -> a
fold
  where
    fold :: VersionRange -> a
fold = (VersionRangeF a -> a) -> VersionRange -> a
forall a. (VersionRangeF a -> a) -> VersionRange -> a
cataVersionRange VersionRangeF a -> a
alg

    alg :: VersionRangeF a -> a
alg VersionRangeF a
AnyVersionF                     = a
anyv
    alg (ThisVersionF Version
v)                = Version -> a
this Version
v
    alg (LaterVersionF Version
v)               = Version -> a
later Version
v
    alg (OrLaterVersionF Version
v)             = a -> a -> a
union (Version -> a
this Version
v) (Version -> a
later Version
v)
    alg (EarlierVersionF Version
v)             = Version -> a
earlier Version
v
    alg (OrEarlierVersionF Version
v)           = a -> a -> a
union (Version -> a
this Version
v) (Version -> a
earlier Version
v)
    alg (WildcardVersionF Version
v)            = VersionRange -> a
fold (Version -> VersionRange
wildcard Version
v)
    alg (MajorBoundVersionF Version
v)          = VersionRange -> a
fold (Version -> VersionRange
majorBound Version
v)
    alg (UnionVersionRangesF a
v1 a
v2)     = a -> a -> a
union a
v1 a
v2
    alg (IntersectVersionRangesF a
v1 a
v2) = a -> a -> a
intersect a
v1 a
v2
    alg (VersionRangeParensF a
v)         = a
v

    wildcard :: Version -> VersionRange
wildcard Version
v = VersionRange -> VersionRange -> VersionRange
intersectVersionRanges
                   (Version -> VersionRange
orLaterVersion Version
v)
                   (Version -> VersionRange
earlierVersion (Version -> Version
wildcardUpperBound Version
v))

    majorBound :: Version -> VersionRange
majorBound Version
v = VersionRange -> VersionRange -> VersionRange
intersectVersionRanges
                     (Version -> VersionRange
orLaterVersion Version
v)
                     (Version -> VersionRange
earlierVersion (Version -> Version
majorUpperBound Version
v))

-- | Normalise 'VersionRange'.
--
-- In particular collapse @(== v || > v)@ into @>= v@, and so on.
normaliseVersionRange :: VersionRange -> VersionRange
normaliseVersionRange :: VersionRange -> VersionRange
normaliseVersionRange = (VersionRangeF VersionRange -> VersionRange)
-> (VersionRange -> VersionRangeF VersionRange)
-> VersionRange
-> VersionRange
hyloVersionRange VersionRangeF VersionRange -> VersionRange
embed VersionRange -> VersionRangeF VersionRange
projectVersionRange
  where
    -- == v || > v, > v || == v  ==>  >= v
    embed :: VersionRangeF VersionRange -> VersionRange
embed (UnionVersionRangesF (ThisVersion Version
v) (LaterVersion Version
v')) | Version
v Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
v' =
        Version -> VersionRange
orLaterVersion Version
v
    embed (UnionVersionRangesF (LaterVersion Version
v) (ThisVersion Version
v')) | Version
v Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
v' =
        Version -> VersionRange
orLaterVersion Version
v

    -- == v || < v, < v || == v  ==>  <= v
    embed (UnionVersionRangesF (ThisVersion Version
v) (EarlierVersion Version
v')) | Version
v Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
v' =
        Version -> VersionRange
orEarlierVersion Version
v
    embed (UnionVersionRangesF (EarlierVersion Version
v) (ThisVersion Version
v')) | Version
v Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
v' =
        Version -> VersionRange
orEarlierVersion Version
v

    -- otherwise embed normally
    embed VersionRangeF VersionRange
vr = VersionRangeF VersionRange -> VersionRange
embedVersionRange VersionRangeF VersionRange
vr

-- |  Remove 'VersionRangeParens' constructors.
--
-- @since 2.2
stripParensVersionRange :: VersionRange -> VersionRange
stripParensVersionRange :: VersionRange -> VersionRange
stripParensVersionRange = (VersionRangeF VersionRange -> VersionRange)
-> (VersionRange -> VersionRangeF VersionRange)
-> VersionRange
-> VersionRange
hyloVersionRange VersionRangeF VersionRange -> VersionRange
embed VersionRange -> VersionRangeF VersionRange
projectVersionRange
  where
    embed :: VersionRangeF VersionRange -> VersionRange
embed (VersionRangeParensF VersionRange
vr) = VersionRange
vr
    embed VersionRangeF VersionRange
vr = VersionRangeF VersionRange -> VersionRange
embedVersionRange VersionRangeF VersionRange
vr

-- | Does this version fall within the given range?
--
-- This is the evaluation function for the 'VersionRange' type.
--
withinRange :: Version -> VersionRange -> Bool
withinRange :: Version -> VersionRange -> Bool
withinRange Version
v = Bool
-> (Version -> Bool)
-> (Version -> Bool)
-> (Version -> Bool)
-> (Bool -> Bool -> Bool)
-> (Bool -> Bool -> Bool)
-> VersionRange
-> Bool
forall a.
a
-> (Version -> a)
-> (Version -> a)
-> (Version -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> VersionRange
-> a
foldVersionRange
                   Bool
True
                   (\Version
v'  -> Version
v Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
v')
                   (\Version
v'  -> Version
v Version -> Version -> Bool
forall a. Ord a => a -> a -> Bool
>  Version
v')
                   (\Version
v'  -> Version
v Version -> Version -> Bool
forall a. Ord a => a -> a -> Bool
<  Version
v')
                   Bool -> Bool -> Bool
(||)
                   Bool -> Bool -> Bool
(&&)

----------------------------
-- Wildcard range utilities
--

-- | @since 2.2
wildcardUpperBound :: Version -> Version
wildcardUpperBound :: Version -> Version
wildcardUpperBound = ([Int] -> [Int]) -> Version -> Version
alterVersion (([Int] -> [Int]) -> Version -> Version)
-> ([Int] -> [Int]) -> Version -> Version
forall a b. (a -> b) -> a -> b
$
    \[Int]
lowerBound -> [Int] -> [Int]
forall a. [a] -> [a]
init [Int]
lowerBound [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [[Int] -> Int
forall a. [a] -> a
last [Int]
lowerBound Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1]

isWildcardRange :: Version -> Version -> Bool
isWildcardRange :: Version -> Version -> Bool
isWildcardRange Version
ver1 Version
ver2 = [Int] -> [Int] -> Bool
forall a. (Eq a, Num a) => [a] -> [a] -> Bool
check (Version -> [Int]
versionNumbers Version
ver1) (Version -> [Int]
versionNumbers Version
ver2)
  where check :: [a] -> [a] -> Bool
check (a
n:[]) (a
m:[]) | a
na -> a -> a
forall a. Num a => a -> a -> a
+a
1 a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
m = Bool
True
        check (a
n:[a]
ns) (a
m:[a]
ms) | a
n   a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
m = [a] -> [a] -> Bool
check [a]
ns [a]
ms
        check [a]
_      [a]
_                 = Bool
False

-- | Does the version range have an upper bound?
--
-- @since 1.24.0.0
hasUpperBound :: VersionRange -> Bool
hasUpperBound :: VersionRange -> Bool
hasUpperBound = Bool
-> (Version -> Bool)
-> (Version -> Bool)
-> (Version -> Bool)
-> (Bool -> Bool -> Bool)
-> (Bool -> Bool -> Bool)
-> VersionRange
-> Bool
forall a.
a
-> (Version -> a)
-> (Version -> a)
-> (Version -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> VersionRange
-> a
foldVersionRange
                Bool
False
                (Bool -> Version -> Bool
forall a b. a -> b -> a
const Bool
True)
                (Bool -> Version -> Bool
forall a b. a -> b -> a
const Bool
False)
                (Bool -> Version -> Bool
forall a b. a -> b -> a
const Bool
True)
                Bool -> Bool -> Bool
(&&) Bool -> Bool -> Bool
(||)

-- | Does the version range have an explicit lower bound?
--
-- Note: this function only considers the user-specified lower bounds, but not
-- the implicit >=0 lower bound.
--
-- @since 1.24.0.0
hasLowerBound :: VersionRange -> Bool
hasLowerBound :: VersionRange -> Bool
hasLowerBound = Bool
-> (Version -> Bool)
-> (Version -> Bool)
-> (Version -> Bool)
-> (Bool -> Bool -> Bool)
-> (Bool -> Bool -> Bool)
-> VersionRange
-> Bool
forall a.
a
-> (Version -> a)
-> (Version -> a)
-> (Version -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> VersionRange
-> a
foldVersionRange
                Bool
False
                (Bool -> Version -> Bool
forall a b. a -> b -> a
const Bool
True)
                (Bool -> Version -> Bool
forall a b. a -> b -> a
const Bool
True)
                (Bool -> Version -> Bool
forall a b. a -> b -> a
const Bool
False)
                Bool -> Bool -> Bool
(&&) Bool -> Bool -> Bool
(||)