Add code for comparing complex version strings

This commit is contained in:
Allan Odgaard
2013-07-01 12:45:15 +02:00
parent 8b696444c6
commit dc1a53aa38
3 changed files with 148 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
#include "version_compare.h"
static bool is_numeric (std::string const& str)
{
return str.find_first_not_of("0123456789") == std::string::npos;
}
static std::vector<std::string> components (std::string const& str)
{
std::vector<std::string> res;
for(size_t from = 0; from < str.size(); )
{
size_t to = str.find_first_of(".-+", from);
if(to != std::string::npos)
{
res.push_back(str.substr(from, to - from));
res.push_back(str.substr(to, 1));
from = to + 1;
}
else
{
res.push_back(str.substr(from));
break;
}
}
return res;
}
static std::vector<std::string> strip_trailing_zeroes (std::vector<std::string> const& src)
{
auto last = std::find_if_not(src.begin(), src.end(), [](std::string const& str){ return is_numeric(str) || str == "."; });
auto from = last;
while(last != src.begin() && is_numeric(*(last-1)) && std::stol(*(last-1)) == 0 && --last != src.begin())
--last;
std::vector<std::string> res;
res.insert(res.end(), src.begin(), last);
res.insert(res.end(), from, src.end());
return res;
}
bool version_less (std::string const& lhs, std::string const& rhs)
{
auto lhsV = strip_trailing_zeroes(components(lhs));
auto rhsV = strip_trailing_zeroes(components(rhs));
auto lhsIt = lhsV.begin();
auto rhsIt = rhsV.begin();
while(lhsIt != lhsV.end() && rhsIt != rhsV.end())
{
bool numberCompare = is_numeric(*lhsIt) && is_numeric(*rhsIt);
if(*lhsIt != *rhsIt && (!numberCompare || std::stol(*lhsIt) != std::stol(*rhsIt)))
{
if(numberCompare)
return std::stol(*lhsIt) < std::stol(*rhsIt);
else if(lhsIt->find_first_not_of(".-+") == std::string::npos)
return *lhsIt == "-" || (*lhsIt == "+" && *rhsIt == ".");
return *lhsIt < *rhsIt;
}
else if(*lhsIt == "+")
{
return false;
}
++lhsIt;
++rhsIt;
}
return lhsIt != lhsV.end() ? *lhsIt == "-" : (rhsIt != rhsV.end() && *rhsIt == ".");
}

View File

@@ -0,0 +1,11 @@
#ifndef VERSION_COMPARE_H_FY5OTOGA
#define VERSION_COMPARE_H_FY5OTOGA
bool version_less (std::string const& rhs, std::string const& lhs);
inline bool version_equal (std::string const& rhs, std::string const& lhs)
{
return !version_less(lhs, rhs) && !version_less(rhs, lhs);
}
#endif /* end of include guard: VERSION_COMPARE_H_FY5OTOGA */

View File

@@ -0,0 +1,63 @@
#include "../src/version_compare.h"
static bool less (std::string const& lhs, std::string const& rhs)
{
return version_less(lhs, rhs) && !version_less(rhs, lhs);
}
static bool equal (std::string const& lhs, std::string const& rhs)
{
return version_equal(lhs, rhs);
}
static bool greator (std::string const& lhs, std::string const& rhs)
{
return !less(lhs, rhs);
}
void test_trailing_zero ()
{
OAK_ASSERT( less("2-beta", "2.0"));
OAK_ASSERT( less("2.0-beta", "2.0"));
OAK_ASSERT( less("2.0.0-beta", "2.0"));
OAK_ASSERT( equal("2", "2.0"));
OAK_ASSERT( equal("2", "2.0+git.hash"));
OAK_ASSERT( equal("2+git.hash", "2.0"));
OAK_ASSERT(greator("2.0.1-beta", "2.0"));
OAK_ASSERT(greator("2.1-beta", "2.0"));
}
void test_exhaustive ()
{
std::string const numbers[] = { "1", "1.01", "1.1.1", "1.1.2", "1.2", "1.2.1", "1.10", "2", "2.1", "2.1.1", "2.2" };
std::vector<std::string> versions;
for(auto number : numbers)
{
versions.push_back(number + "-alpha");
versions.push_back(number + "-alpha.1");
versions.push_back(number + "-alpha.2");
versions.push_back(number + "-beta");
versions.push_back(number + "-beta.1");
versions.push_back(number + "-beta.2");
versions.push_back(number + "-rc.1");
versions.push_back(number);
}
for(size_t i = 0; i < versions.size(); ++i)
{
for(size_t j = i; j < versions.size(); ++j)
{
for(auto lhs : { versions[i], versions[i] + "+git.c0de" })
{
for(auto rhs : { versions[j], versions[j] + "+git.b337" })
{
auto msg = text::format("%s %c %s", lhs.c_str(), i < j ? '<' : (i == j ? '=' : '>'), rhs.c_str());
if(i < j) { OAK_MASSERT(msg, less(lhs, rhs)); }
if(i == j) { OAK_MASSERT(msg, equal(lhs, rhs)); }
if(i > j) { OAK_MASSERT(msg, greator(lhs, rhs)); }
}
}
}
}
}