forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVersion.hs
More file actions
141 lines (120 loc) · 4.54 KB
/
Copy pathVersion.hs
File metadata and controls
141 lines (120 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
-- | Versions for packages.
module Stack.Types.Version
( Cabal.VersionRange -- TODO in the future should have a newtype wrapper
, IntersectingVersionRange (..)
, VersionCheck (..)
, versionRangeText
, Cabal.withinRange
, Stack.Types.Version.intersectVersionRanges
, toMajorVersion
, latestApplicableVersion
, checkVersion
, nextMajorVersion
, minorVersion
, stackVersion
, showStackVersion
, stackMajorVersion
, stackMinorVersion
) where
import Data.List ( find )
import qualified Data.Set as Set
import qualified Data.Text as T
import Data.Version ( showVersion )
import Distribution.Pretty ( pretty )
import qualified Distribution.Version as Cabal
import Pantry.Internal.AesonExtended
( FromJSON (..), ToJSON (..), Value (..), withText )
import qualified Paths_stack as Meta
import Stack.Prelude hiding ( Vector, pretty )
import Text.PrettyPrint ( render )
newtype IntersectingVersionRange = IntersectingVersionRange
{ getIntersectingVersionRange :: Cabal.VersionRange }
deriving Show
instance Semigroup IntersectingVersionRange where
IntersectingVersionRange l <> IntersectingVersionRange r =
IntersectingVersionRange (l `Cabal.intersectVersionRanges` r)
instance Monoid IntersectingVersionRange where
mempty = IntersectingVersionRange Cabal.anyVersion
mappend = (<>)
-- | Display a version range
versionRangeText :: Cabal.VersionRange -> Text
versionRangeText = T.pack . render . pretty
-- | A modified intersection which also simplifies, for better display.
intersectVersionRanges ::
Cabal.VersionRange
-> Cabal.VersionRange
-> Cabal.VersionRange
intersectVersionRanges x y =
Cabal.simplifyVersionRange $ Cabal.intersectVersionRanges x y
-- | Returns the first two components, defaulting to 0 if not present
toMajorVersion :: Version -> Version
toMajorVersion v =
case Cabal.versionNumbers v of
[] -> Cabal.mkVersion [0, 0]
[a] -> Cabal.mkVersion [a, 0]
a:b:_ -> Cabal.mkVersion [a, b]
-- | Given a version range and a set of versions, find the latest version from
-- the set that is within the range.
latestApplicableVersion :: Cabal.VersionRange -> Set Version -> Maybe Version
latestApplicableVersion r = find (`Cabal.withinRange` r) . Set.toDescList
-- | Get the next major version number for the given version
nextMajorVersion :: Version -> Version
nextMajorVersion v =
case Cabal.versionNumbers v of
[] -> Cabal.mkVersion [0, 1]
[a] -> Cabal.mkVersion [a, 1]
a:b:_ -> Cabal.mkVersion [a, b + 1]
data VersionCheck
= MatchMinor
| MatchExact
| NewerMinor
deriving (Eq, Ord, Show)
instance ToJSON VersionCheck where
toJSON MatchMinor = String "match-minor"
toJSON MatchExact = String "match-exact"
toJSON NewerMinor = String "newer-minor"
instance FromJSON VersionCheck where
parseJSON = withText expected $ \t ->
case t of
"match-minor" -> pure MatchMinor
"match-exact" -> pure MatchExact
"newer-minor" -> pure NewerMinor
_ -> fail ("Expected " ++ expected ++ ", but got " ++ show t)
where
expected = "VersionCheck value (match-minor, match-exact, or newer-minor)"
checkVersion :: VersionCheck -> Version -> Version -> Bool
checkVersion check (Cabal.versionNumbers -> wanted) (Cabal.versionNumbers -> actual) =
case check of
MatchMinor -> and (take 3 matching)
MatchExact -> length wanted == length actual && and matching
NewerMinor -> and (take 2 matching) && newerMinor
where
matching = zipWith (==) wanted actual
getMinor (_a:_b:c:_) = Just c
getMinor _ = Nothing
newerMinor =
case (getMinor wanted, getMinor actual) of
(Nothing, _) -> True
(Just _, Nothing) -> False
(Just w, Just a) -> a >= w
-- | Get minor version (excludes any patchlevel)
minorVersion :: Version -> Version
minorVersion = Cabal.mkVersion . take 3 . Cabal.versionNumbers
-- | Current Stack version
stackVersion :: Version
stackVersion = Cabal.mkVersion' Meta.version
-- | Current Stack version in the same format as yielded by
-- 'Data.Version.showVersion'.
showStackVersion :: String
showStackVersion = showVersion Meta.version
-- | Current Stack minor version (excludes patchlevel)
stackMinorVersion :: Version
stackMinorVersion = minorVersion stackVersion
-- | Current Stack major version. Returns the first two components, defaulting
-- to 0 if not present
stackMajorVersion :: Version
stackMajorVersion = toMajorVersion stackVersion