Skip to content

Commit 99c619a

Browse files
committed
preserve pre-release and build parts of a version
preserve pre-release and build parts of a version on coerce
1 parent d054c80 commit 99c619a

File tree

6 files changed

+190
-34
lines changed

6 files changed

+190
-34
lines changed

.abapgit.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
33
<asx:values>
44
<DATA>
5+
<NAME>semver</NAME>
56
<MASTER_LANGUAGE>E</MASTER_LANGUAGE>
67
<STARTING_FOLDER>/src/</STARTING_FOLDER>
78
<FOLDER_LOGIC>PREFIX</FOLDER_LOGIC>
9+
<REQUIREMENTS>
10+
<item>
11+
<COMPONENT>SAP_BASIS</COMPONENT>
12+
<MIN_RELEASE>740</MIN_RELEASE>
13+
</item>
14+
</REQUIREMENTS>
15+
<VERSION_CONSTANT>ZIF_SEMVER_CONSTANTS=&gt;VERSION</VERSION_CONSTANT>
816
</DATA>
917
</asx:values>
1018
</asx:abap>

src/classes/zcl_semver.clas.abap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ CLASS zcl_semver IMPLEMENTATION.
110110
IF semver->version = version.
111111
result = 0.
112112
ELSE.
113-
result = compare_main( other ).
113+
result = compare_main( semver ).
114114
IF result = 0.
115-
result = compare_pre( other ).
115+
result = compare_pre( semver ).
116116
ENDIF.
117117
ENDIF.
118118

src/functions/zcl_semver_functions.clas.abap

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -325,44 +325,60 @@ CLASS zcl_semver_functions IMPLEMENTATION.
325325

326326
TYPES:
327327
BEGIN OF ty_match,
328-
major TYPE string,
329-
minor TYPE string,
330-
patch TYPE string,
331-
offset TYPE i,
332-
length TYPE i,
333-
endpos TYPE i,
328+
major TYPE string,
329+
minor TYPE string,
330+
patch TYPE string,
331+
prerelease TYPE string,
332+
build TYPE string,
333+
offset TYPE i,
334+
length TYPE i,
335+
endpos TYPE i,
334336
END OF ty_match.
335337

336338
DATA matches TYPE STANDARD TABLE OF ty_match.
337339

338340
" cl_abap_matcher has a problem with '1.2.3.4.5.6' so we use FIND REGEX
339341

340-
DATA(r) = COND #(
341-
WHEN rtl = abap_true
342-
THEN zcl_semver_re=>token-coercertl-safe_src
343-
ELSE zcl_semver_re=>token-coerce-safe_src ).
344-
345342
IF rtl = abap_false.
346-
FIND REGEX r IN version SUBMATCHES DATA(rest) DATA(major) DATA(minor) DATA(patch).
343+
DATA(r) = COND #(
344+
WHEN incpre = abap_true
345+
THEN zcl_semver_re=>token-coercefull-safe_src
346+
ELSE zcl_semver_re=>token-coerce-safe_src ).
347+
348+
FIND REGEX r IN version SUBMATCHES DATA(rest) DATA(major) DATA(minor) DATA(patch) DATA(prerelease) DATA(build).
347349
IF sy-subrc <> 0.
348350
RETURN.
349351
ENDIF.
350352
ELSE.
351353
" Find the right-most coercible string that does not share
352354
" a terminus with a more left-ward coercible string.
353355
" Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4'
356+
r = COND #(
357+
WHEN incpre = abap_true
358+
THEN zcl_semver_re=>token-coercertlfull-safe_src
359+
ELSE zcl_semver_re=>token-coercertl-safe_src ).
360+
354361
DATA(offset) = 0.
355362
DO.
356-
FIND REGEX r IN version+offset(*) SUBMATCHES rest major minor patch.
363+
FIND REGEX r IN version+offset(*) SUBMATCHES rest major minor patch prerelease build.
357364
IF sy-subrc <> 0.
358365
EXIT.
359366
ENDIF.
360367
INSERT INITIAL LINE INTO TABLE matches ASSIGNING FIELD-SYMBOL(<match>).
361-
<match>-major = major.
362-
<match>-minor = minor.
363-
<match>-patch = patch.
368+
<match>-major = major.
369+
<match>-minor = minor.
370+
<match>-patch = patch.
371+
<match>-prerelease = prerelease.
372+
<match>-build = build.
373+
374+
DATA(match) = |{ major }|
375+
&& |{ COND #( WHEN minor IS NOT INITIAL THEN '.' && minor ) }|
376+
&& |{ COND #( WHEN patch IS NOT INITIAL THEN '.' && patch ) }|
377+
&& |{ COND #( WHEN prerelease IS NOT INITIAL THEN '-' && prerelease ) }|
378+
&& |{ COND #( WHEN build IS NOT INITIAL THEN '+' && build ) }|.
379+
364380
<match>-offset = offset.
365-
<match>-length = strlen( |{ major }{ COND #( WHEN minor IS NOT INITIAL THEN '.' && minor ) }{ COND #( WHEN patch IS NOT INITIAL THEN '.' && patch ) }| ).
381+
<match>-length = strlen( match ).
366382
<match>-endpos = <match>-offset + <match>-length.
367383
FIND REGEX '^\d' IN version+offset(*) MATCH OFFSET DATA(next_offset).
368384
offset += next_offset + 1.
@@ -375,9 +391,11 @@ CLASS zcl_semver_functions IMPLEMENTATION.
375391
IF sy-subrc <> 0.
376392
EXIT.
377393
ENDIF.
378-
major = <match>-major.
379-
minor = <match>-minor.
380-
patch = <match>-patch.
394+
major = <match>-major.
395+
minor = <match>-minor.
396+
patch = <match>-patch.
397+
prerelease = <match>-prerelease.
398+
build = <match>-build.
381399
ENDIF.
382400

383401
IF minor IS INITIAL.
@@ -388,7 +406,19 @@ CLASS zcl_semver_functions IMPLEMENTATION.
388406
patch = '0'.
389407
ENDIF.
390408

391-
result = parse( version = |{ major }.{ minor }.{ patch }| loose = loose incpre = incpre ).
409+
IF incpre = abap_true AND prerelease IS NOT INITIAL.
410+
prerelease = |-{ prerelease }|.
411+
ELSE.
412+
prerelease = ''.
413+
ENDIF.
414+
415+
IF incpre = abap_true AND build IS NOT INITIAL.
416+
build = |+{ build }|.
417+
ELSE.
418+
build = ''.
419+
ENDIF.
420+
421+
result = parse( version = |{ major }.{ minor }.{ patch }{ prerelease }{ build }| loose = loose incpre = incpre ).
392422

393423
ENDMETHOD.
394424

src/functions/zcl_semver_functions.clas.testclasses.abap

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ CLASS ltcl_semver_functions DEFINITION FOR TESTING RISK LEVEL HARMLESS
1818
cmp_equality FOR TESTING RAISING zcx_semver_error,
1919
coerce_to_null FOR TESTING RAISING zcx_semver_error,
2020
coerce_to_valid FOR TESTING RAISING zcx_semver_error,
21+
coerce_to_valid_incpre FOR TESTING RAISING zcx_semver_error,
2122
coerce_rtl FOR TESTING RAISING zcx_semver_error,
23+
coerce_rtl_incpre FOR TESTING RAISING zcx_semver_error,
2224
compare FOR TESTING RAISING zcx_semver_error,
2325
compare_build FOR TESTING RAISING zcx_semver_error,
2426
compare_loose FOR TESTING RAISING zcx_semver_error,
@@ -60,8 +62,7 @@ CLASS ltcl_semver_functions IMPLEMENTATION.
6062
( range = '>1.2.3' version = '' )
6163
( range = '~1.2.3' version = '' )
6264
( range = '<=1.2.3' version = '' )
63-
( range = '1.2.x' version = '' )
64-
( range = '0.12.0-dev.1150+3c22cecee' version = '0.12.0-dev.1150' ) ).
65+
( range = '1.2.x' version = '' ) ).
6566

6667
LOOP AT tests INTO DATA(test).
6768
cl_abap_unit_assert=>assert_equals(
@@ -278,15 +279,70 @@ CLASS ltcl_semver_functions IMPLEMENTATION.
278279

279280
LOOP AT tests INTO DATA(test).
280281
DATA(semver) = zcl_semver_functions=>coerce( test-version ).
282+
DATA(expected) = zcl_semver_functions=>parse( test-res ).
281283

282284
cl_abap_unit_assert=>assert_bound(
283285
act = semver
284286
msg = |{ test-version } { test-res }| ).
285287

286288
cl_abap_unit_assert=>assert_equals(
287-
act = semver->version
288-
exp = test-res
289+
act = expected->compare( semver )
290+
exp = 0
291+
msg = |{ test-version } should be equal to { test-res }| ).
292+
293+
cl_abap_unit_assert=>assert_equals(
294+
act = expected->compare_build( semver )
295+
exp = 0
296+
msg = |{ test-version } build should be equal to { test-res }| ).
297+
ENDLOOP.
298+
299+
ENDMETHOD.
300+
301+
METHOD coerce_to_valid_incpre.
302+
303+
TYPES:
304+
BEGIN OF ty_test,
305+
version TYPE string,
306+
res TYPE string,
307+
END OF ty_test.
308+
309+
DATA tests TYPE TABLE OF ty_test.
310+
311+
tests = VALUE #(
312+
( version = '1-rc.5' res = '1.0.0-rc.5' )
313+
( version = '1.2-rc.5' res = '1.2.0-rc.5' )
314+
( version = '1.2.3-rc.5' res = '1.2.3-rc.5' )
315+
( version = '1.2.3-rc.5/a' res = '1.2.3-rc.5' )
316+
( version = '1.2.3.4-rc.5' res = '1.2.3' )
317+
( version = '1.2.3.4+rev.6' res = '1.2.3' )
318+
( version = '1+rev.6' res = '1.0.0+rev.6' )
319+
( version = '1.2+rev.6' res = '1.2.0+rev.6' )
320+
( version = '1.2.3+rev.6' res = '1.2.3+rev.6' )
321+
( version = '1.2.3+rev.6/a' res = '1.2.3+rev.6' )
322+
( version = '1.2.3.4-rc.5' res = '1.2.3' )
323+
( version = '1.2.3.4+rev.6' res = '1.2.3' )
324+
( version = '1-rc.5+rev.6' res = '1.0.0-rc.5+rev.6' )
325+
( version = '1.2-rc.5+rev.6' res = '1.2.0-rc.5+rev.6' )
326+
( version = '1.2.3-rc.5+rev.6' res = '1.2.3-rc.5+rev.6' )
327+
( version = '1.2.3-rc.5+rev.6/a' res = '1.2.3-rc.5+rev.6' ) ).
328+
329+
LOOP AT tests INTO DATA(test).
330+
DATA(semver) = zcl_semver_functions=>coerce( version = test-version incpre = abap_true ).
331+
DATA(expected) = zcl_semver_functions=>parse( version = test-res incpre = abap_true ).
332+
333+
cl_abap_unit_assert=>assert_bound(
334+
act = semver
289335
msg = |{ test-version } { test-res }| ).
336+
337+
cl_abap_unit_assert=>assert_equals(
338+
act = expected->compare( semver )
339+
exp = 0
340+
msg = |{ test-version } should be equal to { test-res }| ).
341+
342+
cl_abap_unit_assert=>assert_equals(
343+
act = expected->compare_build( semver )
344+
exp = 0
345+
msg = |{ test-version } build should be equal to { test-res }| ).
290346
ENDLOOP.
291347

292348
ENDMETHOD.
@@ -314,15 +370,62 @@ CLASS ltcl_semver_functions IMPLEMENTATION.
314370

315371
LOOP AT tests INTO DATA(test).
316372
DATA(semver) = zcl_semver_functions=>coerce( version = test-version rtl = abap_true ).
373+
DATA(expected) = zcl_semver_functions=>parse( version = test-res ).
317374

318375
cl_abap_unit_assert=>assert_bound(
319376
act = semver
320377
msg = |{ test-version } { test-res }| ).
321378

322379
cl_abap_unit_assert=>assert_equals(
323-
act = semver->version
324-
exp = test-res
380+
act = expected->compare( semver )
381+
exp = 0
382+
msg = |{ test-version } should be equal to { test-res }| ).
383+
384+
cl_abap_unit_assert=>assert_equals(
385+
act = expected->compare_build( semver )
386+
exp = 0
387+
msg = |{ test-version } build should be equal to { test-res }| ).
388+
ENDLOOP.
389+
390+
ENDMETHOD.
391+
392+
METHOD coerce_rtl_incpre.
393+
394+
TYPES:
395+
BEGIN OF ty_test,
396+
version TYPE string,
397+
res TYPE string,
398+
END OF ty_test.
399+
400+
DATA tests TYPE TABLE OF ty_test.
401+
402+
tests = VALUE #(
403+
( version = '1.2-rc.5+rev.6' res = '1.2.0-rc.5+rev.6' )
404+
( version = '1.2.3-rc.5+rev.6' res = '1.2.3-rc.5+rev.6' )
405+
( version = '1.2.3.4-rc.5+rev.6' res = '2.3.4-rc.5+rev.6' )
406+
( version = '1.2.3.4-rc.5' res = '2.3.4-rc.5' )
407+
( version = '1.2.3.4+rev.6' res = '2.3.4+rev.6' )
408+
( version = '1.2.3.4-rc.5+rev.6/7' res = '7.0.0' )
409+
( version = '1.2.3.4-rc/7.5+rev.6' res = '7.5.0+rev.6' )
410+
( version = '1.2.3.4/7-rc.5+rev.6' res = '7.0.0-rc.5+rev.6' ) ).
411+
412+
LOOP AT tests INTO DATA(test).
413+
DATA(semver) = zcl_semver_functions=>coerce( version = test-version rtl = abap_true incpre = abap_true ).
414+
DATA(expected) = zcl_semver_functions=>parse( version = test-res incpre = abap_true ).
415+
416+
cl_abap_unit_assert=>assert_bound(
417+
act = semver
325418
msg = |{ test-version } { test-res }| ).
419+
420+
cl_abap_unit_assert=>assert_equals(
421+
act = expected->compare( semver )
422+
exp = 0
423+
msg = |{ test-version } should be equal to { test-res }| ).
424+
425+
cl_abap_unit_assert=>assert_equals(
426+
act = expected->compare_build( semver )
427+
exp = 0
428+
msg = |{ test-version } build should be equal to { test-res }| ).
326429
ENDLOOP.
327430

328431
ENDMETHOD.

src/internal/zcl_semver_re.clas.abap

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ CLASS zcl_semver_re DEFINITION
4242
caretloose TYPE ty_token,
4343
carettrim TYPE ty_token,
4444
coerce TYPE ty_token,
45+
coercefull TYPE ty_token,
46+
coerceplain TYPE ty_token,
4547
coercertl TYPE ty_token,
48+
coercertlfull TYPE ty_token,
4649
comparator TYPE ty_token,
4750
comparatorloose TYPE ty_token,
4851
comparatortrim TYPE ty_token,
@@ -266,15 +269,27 @@ CLASS zcl_semver_re IMPLEMENTATION.
266269
" Extract anything that could conceivably be a part of a valid semver
267270

268271
create_token(
269-
name = 'COERCE'
272+
name = 'COERCEPLAIN'
270273
value = |(^\|[^\\d])(\\d\{1,{ zif_semver_constants=>max_safe_component_length }\})| &&
271274
|(?:\\.(\\d\{1,{ zif_semver_constants=>max_safe_component_length }\}))?| &&
272-
|(?:\\.(\\d\{1,{ zif_semver_constants=>max_safe_component_length }\}))?| &&
275+
|(?:\\.(\\d\{1,{ zif_semver_constants=>max_safe_component_length }\}))?| ).
276+
create_token(
277+
name = 'COERCE'
278+
value = |{ token-coerceplain-src }(?:$\|[^\\d])| ).
279+
create_token(
280+
name = 'COERCEFULL'
281+
value = |{ token-coerceplain-src }| &&
282+
|(?:{ token-prerelease-src })?| &&
283+
|(?:{ token-build-src })?| &&
273284
|(?:$\|[^\\d])| ).
274285
create_token(
275286
name = 'COERCERTL'
276287
value = token-coerce-src
277288
is_global = abap_true ).
289+
create_token(
290+
name = 'COERCERTLFULL'
291+
value = token-coercefull-src
292+
is_global = abap_true ).
278293

279294
" Tilde ranges.
280295
" Meaning is "reasonably at or greater than"

src/internal/zif_semver_constants.intf.abap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ INTERFACE zif_semver_constants PUBLIC.
77
* ABAP Port by Marc Bernard <https://marcbernardtools.com/>
88
* SPDX-License-Identifier: ISC
99
************************************************************************
10-
* Based on node semver package v7.5.4 (July 2023)
11-
* https://github.com/npm/node-semver/releases/tag/v7.5.4
10+
* Based on node semver package v7.6.0 (January 2024)
11+
* https://github.com/npm/node-semver/releases/tag/v7.6.0
1212
************************************************************************
1313

1414
" Package version
15-
CONSTANTS version TYPE string VALUE '7.5.4' ##NEEDED.
15+
CONSTANTS version TYPE string VALUE '7.6.0' ##NEEDED.
1616

1717
" Note: this is the semver.org version of the spec that it implements
1818
" Not necessarily the package version of this code.

0 commit comments

Comments
 (0)