From 94491ba0d0e18dd4ef475b3b061cf30415e5ebda Mon Sep 17 00:00:00 2001
From: Sergio Zharinov <zharinov@users.noreply.github.com>
Date: Sat, 5 Jan 2019 20:32:55 +0400
Subject: [PATCH] feat: Implement auxiliary functions for maven versioning
 (#3028)

---
 lib/versioning/maven/compare.js | 20 ++++++--
 lib/versioning/maven/index.js   | 82 ++++++++++++++++++++++++++++-----
 test/versioning/maven.spec.js   | 62 +++++++++++++++++++++++++
 3 files changed, 148 insertions(+), 16 deletions(-)

diff --git a/lib/versioning/maven/compare.js b/lib/versioning/maven/compare.js
index 865f1830f0..fa6e77f206 100644
--- a/lib/versioning/maven/compare.js
+++ b/lib/versioning/maven/compare.js
@@ -79,10 +79,17 @@ function isNull(token) {
   return val === 0 || val === '' || val === 'final' || val === 'ga';
 }
 
+const zeroToken = {
+  prefix: PREFIX_HYPHEN,
+  type: TYPE_NUMBER,
+  val: 0,
+  isTransition: false,
+};
+
 function tokenize(versionStr) {
   let buf = [];
   let result = [];
-  iterateTokens(versionStr, token => {
+  iterateTokens(versionStr.toLowerCase().replace(/^v/i, ''), token => {
     if (token.prefix === PREFIX_HYPHEN) {
       buf = [];
     }
@@ -92,7 +99,7 @@ function tokenize(versionStr) {
       buf = [];
     }
   });
-  return result;
+  return result.length ? result : [zeroToken];
 }
 
 function nullFor(token) {
@@ -196,8 +203,8 @@ function tokenCmp(left, right) {
 }
 
 function compare(left, right) {
-  const leftTokens = tokenize(left.toLowerCase());
-  const rightTokens = tokenize(right.toLowerCase());
+  const leftTokens = tokenize(left);
+  const rightTokens = tokenize(right);
   const length = Math.max(leftTokens.length, rightTokens.length);
   for (let idx = 0; idx < length; idx += 1) {
     const leftToken = leftTokens[idx] || nullFor(rightTokens[idx]);
@@ -209,5 +216,10 @@ function compare(left, right) {
 }
 
 module.exports = {
+  PREFIX_DOT,
+  PREFIX_HYPHEN,
+  TYPE_NUMBER,
+  TYPE_QUALIFIER,
+  tokenize,
   compare,
 };
diff --git a/lib/versioning/maven/index.js b/lib/versioning/maven/index.js
index 3ce865ec75..1a2d0971fe 100644
--- a/lib/versioning/maven/index.js
+++ b/lib/versioning/maven/index.js
@@ -1,25 +1,83 @@
 /* istanbul ignore file */
 
-const { compare } = require('./compare');
+const { tokenize, compare, TYPE_NUMBER, TYPE_QUALIFIER } = require('./compare');
+
+const isVersion = version => {
+  if (!version) return false;
+  if (!version.length) return false;
+  if (!/^v?\d[a-z0-9.-]*$/i.test(version)) return false;
+  const tokens = tokenize(version);
+  const majorToken = tokens[0];
+  return !!majorToken && majorToken.type === TYPE_NUMBER;
+};
 
 const equals = (a, b) => compare(a, b) === 0;
 
-// TODO getMajor
-const getMajor = () => 0;
+const getMajor = version => {
+  if (isVersion(version)) {
+    const tokens = tokenize(version);
+    const majorToken = tokens[0];
+    return majorToken.val;
+  }
+  return null;
+};
 
-// TODO getMinor
-const getMinor = () => 0;
+const getMinor = version => {
+  if (isVersion(version)) {
+    const tokens = tokenize(version);
+    const minorToken = tokens[1];
+    if (minorToken && minorToken.type === TYPE_NUMBER) {
+      return minorToken.val;
+    }
+    return 0;
+  }
+  return null;
+};
 
-// TODO getPatch
-const getPatch = () => 0;
+const getPatch = version => {
+  if (isVersion(version)) {
+    const tokens = tokenize(version);
+    const minorToken = tokens[1];
+    const patchToken = tokens[2];
+    if (
+      patchToken &&
+      minorToken.type === TYPE_NUMBER &&
+      patchToken.type === TYPE_NUMBER
+    ) {
+      return patchToken.val;
+    }
+    return 0;
+  }
+  return null;
+};
 
 const isGreaterThan = (a, b) => compare(a, b) === 1;
 
-// TODO isStable
-const isStable = () => true;
-
-// TODO isVersion
-const isVersion = () => true;
+const isStable = version => {
+  if (isVersion(version)) {
+    const tokens = tokenize(version);
+    const qualToken = tokens.find(token => token.type === TYPE_QUALIFIER);
+    if (qualToken) {
+      const val = qualToken.val;
+      if (val === 'final') {
+        return true;
+      }
+      if (val === 'ga') {
+        return true;
+      }
+      if (val === 'release') {
+        return true;
+      }
+      if (val === 'sp') {
+        return true;
+      }
+      // istanbul ignore next
+      return false;
+    }
+    return true;
+  }
+  return null;
+};
 
 const maxSatisfyingVersion = (versions, range) =>
   versions.find(version => equals(version, range));
diff --git a/test/versioning/maven.spec.js b/test/versioning/maven.spec.js
index a3ef2208ec..dc4967c7f0 100644
--- a/test/versioning/maven.spec.js
+++ b/test/versioning/maven.spec.js
@@ -1,4 +1,11 @@
 const { compare } = require('../../lib/versioning/maven/compare');
+const {
+  isVersion,
+  isStable,
+  getMajor,
+  getMinor,
+  getPatch,
+} = require('../../lib/versioning/maven/index');
 
 describe('versioning/maven/compare', () => {
   it('returns equality', () => {
@@ -36,6 +43,7 @@ describe('versioning/maven/compare', () => {
     expect(compare('1-0-ga', '1.0')).toEqual(0);
     expect(compare('1-0-final', '1-0')).toEqual(0);
     expect(compare('1-0', '1.0')).toEqual(0);
+    expect(compare('v1.2.3', '1.2.3')).toEqual(0);
   });
   it('returns less than', () => {
     expect(compare('1', '1.1')).toEqual(-1);
@@ -70,3 +78,57 @@ describe('versioning/maven/compare', () => {
     expect(compare('1', '1-cr1')).toEqual(1);
   });
 });
+
+describe('versioning/maven/index', () => {
+  it('validates version string', () => {
+    expect(isVersion('')).toEqual(false);
+    expect(isVersion('1.0.0')).toEqual(true);
+    expect(isVersion('0')).toEqual(true);
+    expect(isVersion('0.1-2-sp')).toEqual(true);
+    expect(isVersion('1-final')).toEqual(true);
+    expect(isVersion('v1.0.0')).toEqual(true);
+    expect(isVersion('x1.0.0')).toEqual(false);
+  });
+  it('checks if version is stable', () => {
+    expect(isStable('')).toEqual(null);
+    expect(isStable('foobar')).toEqual(null);
+    expect(isStable('1')).toEqual(true);
+    expect(isStable('1.2')).toEqual(true);
+    expect(isStable('1.2.3')).toEqual(true);
+    expect(isStable('1.2.3.4')).toEqual(true);
+    expect(isStable('v1.2.3.4')).toEqual(true);
+    expect(isStable('1-alpha-1')).toEqual(false);
+    expect(isStable('1-b1')).toEqual(false);
+    expect(isStable('1.final')).toEqual(true);
+    expect(isStable('1.0milestone1')).toEqual(false);
+    expect(isStable('1-sp')).toEqual(true);
+    expect(isStable('1-ga-1')).toEqual(true);
+  });
+  it('returns major version', () => {
+    expect(getMajor('')).toEqual(null);
+    expect(getMajor('1')).toEqual(1);
+    expect(getMajor('1.2')).toEqual(1);
+    expect(getMajor('1.2.3')).toEqual(1);
+    expect(getMajor('v1.2.3')).toEqual(1);
+    expect(getMajor('1rc42')).toEqual(1);
+  });
+  it('returns minor version', () => {
+    expect(getMinor('')).toEqual(null);
+    expect(getMinor('1')).toEqual(0);
+    expect(getMinor('1.2')).toEqual(2);
+    expect(getMinor('1.2.3')).toEqual(2);
+    expect(getMinor('v1.2.3')).toEqual(2);
+    expect(getMinor('1.2.3.4')).toEqual(2);
+    expect(getMinor('1-rc42')).toEqual(0);
+  });
+  it('returns patch version', () => {
+    expect(getPatch('')).toEqual(null);
+    expect(getPatch('1')).toEqual(0);
+    expect(getPatch('1.2')).toEqual(0);
+    expect(getPatch('1.2.3')).toEqual(3);
+    expect(getPatch('v1.2.3')).toEqual(3);
+    expect(getPatch('1.2.3.4')).toEqual(3);
+    expect(getPatch('1-rc10')).toEqual(0);
+    expect(getPatch('1-rc42-1')).toEqual(0);
+  });
+});
-- 
GitLab