1module Distribution.Types.VersionRange (
2    -- * Version ranges
3    VersionRange,
4
5    -- ** Constructing
6    anyVersion, noVersion,
7    thisVersion, notThisVersion,
8    laterVersion, earlierVersion,
9    orLaterVersion, orEarlierVersion,
10    unionVersionRanges, intersectVersionRanges,
11    withinVersion,
12    majorBoundVersion,
13
14    -- ** Inspection
15    --
16    -- See "Distribution.Version" for more utilities.
17    withinRange,
18    foldVersionRange,
19    normaliseVersionRange,
20    stripParensVersionRange,
21    hasUpperBound,
22    hasLowerBound,
23
24    -- ** Cata & ana
25    VersionRangeF (..),
26    cataVersionRange,
27    anaVersionRange,
28    hyloVersionRange,
29    projectVersionRange,
30    embedVersionRange,
31
32    -- ** Utilities
33    isAnyVersion,
34    isAnyVersionLight,
35    wildcardUpperBound,
36    majorUpperBound,
37    isWildcardRange,
38    versionRangeParser,
39    ) where
40
41import Distribution.Compat.Prelude
42import Distribution.Types.Version
43import Distribution.Types.VersionInterval
44import Distribution.Types.VersionRange.Internal
45import Prelude ()
46
47-- | Fold over the basic syntactic structure of a 'VersionRange'.
48--
49-- This provides a syntactic view of the expression defining the version range.
50-- The syntactic sugar @\">= v\"@, @\"<= v\"@ and @\"== v.*\"@ is presented
51-- in terms of the other basic syntax.
52--
53-- For a semantic view use 'asVersionIntervals'.
54--
55foldVersionRange :: a                         -- ^ @\"-any\"@ version
56                 -> (Version -> a)            -- ^ @\"== v\"@
57                 -> (Version -> a)            -- ^ @\"> v\"@
58                 -> (Version -> a)            -- ^ @\"< v\"@
59                 -> (a -> a -> a)             -- ^ @\"_ || _\"@ union
60                 -> (a -> a -> a)             -- ^ @\"_ && _\"@ intersection
61                 -> VersionRange -> a
62foldVersionRange _any this later earlier union intersect = fold
63  where
64    fold = cataVersionRange alg
65
66    alg (ThisVersionF v)                = this v
67    alg (LaterVersionF v)               = later v
68    alg (OrLaterVersionF v)             = union (this v) (later v)
69    alg (EarlierVersionF v)             = earlier v
70    alg (OrEarlierVersionF v)           = union (this v) (earlier v)
71    alg (MajorBoundVersionF v)          = fold (majorBound v)
72    alg (UnionVersionRangesF v1 v2)     = union v1 v2
73    alg (IntersectVersionRangesF v1 v2) = intersect v1 v2
74
75    majorBound v = intersectVersionRanges
76                     (orLaterVersion v)
77                     (earlierVersion (majorUpperBound v))
78
79-- | Normalise 'VersionRange'.
80--
81-- In particular collapse @(== v || > v)@ into @>= v@, and so on.
82normaliseVersionRange :: VersionRange -> VersionRange
83normaliseVersionRange = hyloVersionRange embed projectVersionRange
84  where
85    -- == v || > v, > v || == v  ==>  >= v
86    embed (UnionVersionRangesF (ThisVersion v) (LaterVersion v')) | v == v' =
87        orLaterVersion v
88    embed (UnionVersionRangesF (LaterVersion v) (ThisVersion v')) | v == v' =
89        orLaterVersion v
90
91    -- == v || < v, < v || == v  ==>  <= v
92    embed (UnionVersionRangesF (ThisVersion v) (EarlierVersion v')) | v == v' =
93        orEarlierVersion v
94    embed (UnionVersionRangesF (EarlierVersion v) (ThisVersion v')) | v == v' =
95        orEarlierVersion v
96
97    -- otherwise embed normally
98    embed vr = embedVersionRange vr
99
100-- |  Remove 'VersionRangeParens' constructors.
101--
102-- Since version 3.4 this function is 'id', there aren't 'VersionRangeParens' constructor in 'VersionRange' anymore.
103--
104-- @since 2.2
105stripParensVersionRange :: VersionRange -> VersionRange
106stripParensVersionRange = id
107
108-- | Does this version fall within the given range?
109--
110-- This is the evaluation function for the 'VersionRange' type.
111--
112withinRange :: Version -> VersionRange -> Bool
113withinRange v = foldVersionRange
114                   True
115                   (\v'  -> v == v')
116                   (\v'  -> v >  v')
117                   (\v'  -> v <  v')
118                   (||)
119                   (&&)
120
121-- | Does this 'VersionRange' place any restriction on the 'Version' or is it
122-- in fact equivalent to 'AnyVersion'.
123--
124-- Note this is a semantic check, not simply a syntactic check. So for example
125-- the following is @True@ (for all @v@).
126--
127-- > isAnyVersion (EarlierVersion v `UnionVersionRanges` orLaterVersion v)
128--
129isAnyVersion :: VersionRange -> Bool
130isAnyVersion vr = case asVersionIntervals vr of
131    [(LowerBound v InclusiveBound, NoUpperBound)] -> v == version0
132    _                                             -> False
133
134-- A fast and non-precise version of 'isAnyVersion',
135-- returns 'True' only for @>= 0@ 'VersionRange's.
136--
137-- /Do not use/. The "VersionIntervals don't destroy MajorBoundVersion"
138-- https://github.com/haskell/cabal/pull/6736 pull-request
139-- will change 'simplifyVersionRange' to properly preserve semantics.
140-- Then we can use it to normalise 'VersionRange's in tests.
141--
142isAnyVersionLight :: VersionRange -> Bool
143isAnyVersionLight (OrLaterVersion v) = v == version0
144isAnyVersionLight _vr                = False
145
146----------------------------
147-- Wildcard range utilities
148--
149
150
151isWildcardRange :: Version -> Version -> Bool
152isWildcardRange ver1 ver2 = check (versionNumbers ver1) (versionNumbers ver2)
153  where check (n:[]) (m:[]) | n+1 == m = True
154        check (n:ns) (m:ms) | n   == m = check ns ms
155        check _      _                 = False
156
157-- | Does the version range have an upper bound?
158--
159-- @since 1.24.0.0
160hasUpperBound :: VersionRange -> Bool
161hasUpperBound = foldVersionRange
162                False
163                (const True)
164                (const False)
165                (const True)
166                (&&) (||)
167
168-- | Does the version range have an explicit lower bound?
169--
170-- Note: this function only considers the user-specified lower bounds, but not
171-- the implicit >=0 lower bound.
172--
173-- @since 1.24.0.0
174hasLowerBound :: VersionRange -> Bool
175hasLowerBound = foldVersionRange
176                False
177                (const True)
178                (const True)
179                (const False)
180                (&&) (||)
181