Skip to content

Commit 0557ec4

Browse files
fix(semver): compare pre-release strings (#169)
* port rust logic to lua * remove comment * replace with vim.split * revert vim.split changes * vim.split with escape char --------- Co-authored-by: nelsonacsg <nelson@ambercreative.sg>
1 parent e2e1579 commit 0557ec4

File tree

1 file changed

+85
-6
lines changed

1 file changed

+85
-6
lines changed

lua/crates/semver.lua

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,22 +200,101 @@ function M.parse_requirements(str)
200200
return requirements
201201
end
202202

203-
-- TODO: port https://github.com/dtolnay/semver/blob/master/src%2Fimpls.rs#L51-L107
204203
---@param version? string
205204
---@param req? string
206205
---@return integer
207206
function M.compare_pre(version, req)
208-
if version and req then
209-
if version < req then
207+
-- Handle identical strings (optimization)
208+
if version == req then
209+
return 0
210+
end
211+
212+
-- Handle empty prerelease cases
213+
local version_empty = not version or version == ""
214+
local req_empty = not req or req == ""
215+
216+
if version_empty and req_empty then
217+
return 0
218+
elseif version_empty then
219+
-- A real release compares greater than prerelease
220+
return 1
221+
elseif req_empty then
222+
-- Prerelease compares less than the real release
223+
return -1
224+
end
225+
226+
---@param str string
227+
---@return boolean
228+
local function is_numeric(str)
229+
return str:match("^%d+$") ~= nil
230+
end
231+
232+
---@param a string
233+
---@param b string
234+
---@return integer
235+
local function compare_numeric_strings(a, b)
236+
-- First compare by length (shorter number is smaller)
237+
local len_cmp = #a - #b
238+
if len_cmp ~= 0 then
239+
return len_cmp
240+
end
241+
-- If same length, compare lexically
242+
if a < b then
210243
return -1
211-
elseif version == req then
244+
elseif a > b then
245+
return 1
246+
else
212247
return 0
213-
elseif version > req then
248+
end
249+
end
250+
251+
local version_parts = version and vim.split(version, "%.") or {}
252+
local req_parts = req and vim.split(req, "%.") or {}
253+
254+
local max_len = math.max(#version_parts, #req_parts)
255+
256+
for i = 1, max_len do
257+
local version_part = version_parts[i]
258+
local req_part = req_parts[i]
259+
260+
-- If one side has no more parts, the longer one is greater
261+
if not version_part then
262+
return -1
263+
elseif not req_part then
214264
return 1
215265
end
266+
267+
local version_numeric = is_numeric(version_part)
268+
local req_numeric = is_numeric(req_part)
269+
270+
---@type integer
271+
local cmp
272+
if version_numeric and req_numeric then
273+
-- Both numeric: compare numerically
274+
cmp = compare_numeric_strings(version_part, req_part)
275+
elseif version_numeric and not req_numeric then
276+
-- Numeric has lower precedence than non-numeric
277+
cmp = -1
278+
elseif not version_numeric and req_numeric then
279+
-- Non-numeric has higher precedence than numeric
280+
cmp = 1
281+
else
282+
-- Both non-numeric: compare lexically
283+
if version_part < req_part then
284+
cmp = -1
285+
elseif version_part > req_part then
286+
cmp = 1
287+
else
288+
cmp = 0
289+
end
290+
end
291+
292+
if cmp ~= 0 then
293+
return cmp
294+
end
216295
end
217296

218-
return (req and 1 or 0) - (version and 1 or 0)
297+
return 0
219298
end
220299

221300
-- TODO: port https://github.com/dtolnay/semver/blob/master/src/impls.rs#L109-L153

0 commit comments

Comments
 (0)