@@ -3,13 +3,15 @@ module Registry.Version
3
3
, major
4
4
, minor
5
5
, patch
6
- , raw
6
+ , rawVersion
7
7
, printVersion
8
8
, parseVersion
9
9
, Range
10
10
, greaterThanOrEq
11
11
, lessThan
12
12
, printRange
13
+ , rawRange
14
+ , ParseMode (..)
13
15
, parseRange
14
16
) where
15
17
@@ -20,7 +22,9 @@ import Data.Function (on)
20
22
import Data.Int as Int
21
23
import Data.List as List
22
24
import Data.List.NonEmpty as NEL
25
+ import Data.String as String
23
26
import Data.String.CodeUnits as CodeUnits
27
+ import Foreign.SemVer as SemVer
24
28
import Registry.Json as Json
25
29
import Text.Parsing.StringParser (ParseError , Parser )
26
30
import Text.Parsing.StringParser as StringParser
@@ -29,7 +33,13 @@ import Text.Parsing.StringParser.Combinators as StringParser.Combinators
29
33
30
34
-- | A Registry-compliant version of the form 'X.Y.Z', where each place is a
31
35
-- | non-negative integer.
32
- newtype Version = Version { major :: Int , minor :: Int , patch :: Int , raw :: String }
36
+ newtype Version = Version
37
+ { major :: Int
38
+ , minor :: Int
39
+ , patch :: Int
40
+ , mode :: ParseMode
41
+ , raw :: String
42
+ }
33
43
34
44
derive instance Eq Version
35
45
@@ -40,7 +50,7 @@ instance RegistryJson Version where
40
50
encode = Json .encode <<< printVersion
41
51
decode json = do
42
52
string <- Json .decode json
43
- lmap StringParser .printParserError $ parseVersion string
53
+ lmap StringParser .printParserError $ parseVersion Strict string
44
54
45
55
instance Show Version where
46
56
show = printVersion
@@ -54,8 +64,8 @@ minor (Version version) = version.minor
54
64
patch :: Version -> Int
55
65
patch (Version version) = version.patch
56
66
57
- raw :: Version -> String
58
- raw (Version version) = version.raw
67
+ rawVersion :: Version -> String
68
+ rawVersion (Version version) = version.raw
59
69
60
70
printVersion :: Version -> String
61
71
printVersion version = do
@@ -68,15 +78,24 @@ printVersion version = do
68
78
, printInt (patch version)
69
79
]
70
80
71
- parseVersion :: String -> Either ParseError Version
72
- parseVersion input = flip StringParser .runParser input do
81
+ parseVersion :: ParseMode -> String -> Either ParseError Version
82
+ parseVersion mode input = flip StringParser .runParser input do
83
+ -- We allow leading whitespace and the commonly-used 'v' character prefix in
84
+ -- lenient mode.
85
+ when (mode == Lenient ) do
86
+ _ <- StringParser.CodeUnits .whiteSpace
87
+ _ <- StringParser.Combinators .optional $ StringParser.CodeUnits .char ' v'
88
+ pure unit
73
89
major' <- nonNegativeInt
74
90
_ <- StringParser.CodeUnits .char ' .'
75
91
minor' <- nonNegativeInt
76
92
_ <- StringParser.CodeUnits .char ' .'
77
93
patch' <- nonNegativeInt
94
+ when (mode == Lenient ) do
95
+ _ <- StringParser.CodeUnits .whiteSpace
96
+ pure unit
78
97
StringParser.CodeUnits .eof
79
- pure $ Version { major: major', minor: minor', patch: patch', raw: input }
98
+ pure $ Version { major: major', minor: minor', patch: patch', mode, raw: input }
80
99
where
81
100
nonNegativeInt :: Parser Int
82
101
nonNegativeInt = do
@@ -86,24 +105,29 @@ parseVersion input = flip StringParser.runParser input do
86
105
digitString = CodeUnits .fromCharArray $ Array .fromFoldable digitChars
87
106
failInteger = StringParser .fail $ " Invalid 32-bit integer: " <> digitString
88
107
integer <- maybe failInteger pure $ Int .fromString digitString
89
- -- We do not accept leading zeros in versions
90
- when (zeroCount > 1 || (zeroCount == 1 && integer /= 0 )) do
108
+ -- We do not accept leading zeros in versions unless we are in lenient mode
109
+ when (mode == Strict && ( zeroCount > 1 || (zeroCount == 1 && integer /= 0 ) )) do
91
110
StringParser .fail $ " Leading zeros are not allowed: " <> digitString
92
111
when (integer < 0 ) do
93
112
StringParser .fail $ " Invalid non-negative integer: " <> show integer
94
113
pure integer
95
114
96
115
-- | A Registry-compliant version range of the form '>=X.Y.Z <X.Y.Z', where the
97
116
-- | left-hand version is less than the right-hand version.
98
- newtype Range = Range { lhs :: Version , rhs :: Version }
117
+ newtype Range = Range
118
+ { lhs :: Version
119
+ , rhs :: Version
120
+ , mode :: ParseMode
121
+ , raw :: String
122
+ }
99
123
100
124
derive instance Eq Range
101
125
102
126
instance RegistryJson Range where
103
127
encode = Json .encode <<< printRange
104
128
decode json = do
105
129
string <- Json .decode json
106
- lmap StringParser .printParserError $ parseRange string
130
+ lmap StringParser .printParserError $ parseRange Strict string
107
131
108
132
instance Show Range where
109
133
show = printRange
@@ -114,6 +138,9 @@ greaterThanOrEq (Range range) = range.lhs
114
138
lessThan :: Range -> Version
115
139
lessThan (Range range) = range.rhs
116
140
141
+ rawRange :: Range -> String
142
+ rawRange (Range range) = range.raw
143
+
117
144
printRange :: Range -> String
118
145
printRange range =
119
146
Array .fold
@@ -123,25 +150,38 @@ printRange range =
123
150
, printVersion (lessThan range)
124
151
]
125
152
126
- parseRange :: String -> Either ParseError Range
127
- parseRange = StringParser .runParser do
128
- _ <- StringParser.CodeUnits .string " >="
129
- lhs <- toVersion =<< map toString charsUntilSpace
130
- _ <- StringParser.CodeUnits .char ' <'
131
- rhs <- toVersion =<< map toString chars
132
- StringParser.CodeUnits .eof
133
- when (lhs >= rhs) do
134
- StringParser .fail $ Array .fold
135
- [ " Left-hand version ("
136
- , printVersion lhs
137
- , " ) must be less than right-hand version ("
138
- , printVersion rhs
139
- , " )"
140
- ]
141
- pure $ Range { lhs, rhs }
153
+ parseRange :: ParseMode -> String -> Either ParseError Range
154
+ parseRange mode input = do
155
+ let
156
+ strictParser :: Parser Range
157
+ strictParser = do
158
+ _ <- StringParser.CodeUnits .string " >="
159
+ lhs <- toVersion =<< map toString charsUntilSpace
160
+ _ <- StringParser.CodeUnits .char ' <'
161
+ rhs <- toVersion =<< map toString chars
162
+ StringParser.CodeUnits .eof
163
+ when (lhs >= rhs) do
164
+ StringParser .fail $ Array .fold
165
+ [ " Left-hand version ("
166
+ , printVersion lhs
167
+ , " ) must be less than right-hand version ("
168
+ , printVersion rhs
169
+ , " )"
170
+ ]
171
+ pure $ Range { lhs, rhs, mode, raw: input }
172
+
173
+ case mode of
174
+ Lenient -> case SemVer .parseRange input of
175
+ Nothing ->
176
+ Left { pos: 0 , error: " Unable to parse SemVer range in lenient mode." }
177
+ Just parsed -> do
178
+ let trimPrereleaseGuards = String .replaceAll (String.Pattern " -0" ) (String.Replacement " " )
179
+ StringParser .runParser strictParser $ trimPrereleaseGuards parsed
180
+ Strict ->
181
+ StringParser .runParser strictParser input
142
182
where
143
183
toVersion string =
144
- case parseVersion string of
184
+ case parseVersion mode string of
145
185
Left { error } ->
146
186
StringParser .fail error
147
187
Right parsed ->
@@ -158,3 +198,7 @@ parseRange = StringParser.runParser do
158
198
StringParser.Combinators .manyTill
159
199
StringParser.CodeUnits .anyChar
160
200
(StringParser.CodeUnits .char ' ' )
201
+
202
+ data ParseMode = Lenient | Strict
203
+
204
+ derive instance Eq ParseMode
0 commit comments