1 /*
2  * Version.hpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #ifndef CORE_VERSION_HPP
17 #define CORE_VERSION_HPP
18 
19 #include <string>
20 
21 #include <boost/regex.hpp>
22 
23 #include <core/Algorithm.hpp>
24 #include <shared_core/Error.hpp>
25 #include <shared_core/SafeConvert.hpp>
26 #include <core/StringUtils.hpp>
27 
28 namespace rstudio {
29 namespace core {
30 
31 class Version
32 {
33 
34 public:
35 
Version()36    Version()
37    {
38    }
39 
Version(std::string version)40    Version(std::string version)
41    {
42       try
43       {
44          // split version into components
45          static boost::regex reVersion("[._-]+");
46          version = core::string_utils::trimWhitespace(version);
47          boost::sregex_token_iterator it(version.begin(), version.end(), reVersion, -1);
48          boost::sregex_token_iterator end;
49          for (; it != end; ++it)
50          {
51             std::string component = *it;
52             int value = core::safe_convert::stringTo<int>(component, 0);
53             pieces_.push_back(value);
54          }
55       }
56       CATCH_UNEXPECTED_EXCEPTION;
57    }
58 
Version(int versionMajor,int versionMinor,int versionPatch)59    Version(int versionMajor, int versionMinor, int versionPatch)
60    {
61       pieces_.push_back(versionMajor);
62       pieces_.push_back(versionMinor);
63       pieces_.push_back(versionPatch);
64    }
65 
versionMajor() const66    int versionMajor() const
67    {
68       return extractVersion(0);
69    }
70 
versionMinor() const71    int versionMinor() const
72    {
73       return extractVersion(1);
74    }
75 
versionPatch() const76    int versionPatch() const
77    {
78       return extractVersion(2);
79    }
80 
extractVersion(std::size_t index) const81    int extractVersion(std::size_t index) const
82    {
83       if (index < pieces_.size())
84          return pieces_[index];
85 
86       return 0;
87    }
88 
compare(const Version & other) const89    int compare(const Version& other) const
90    {
91       return compare(*this, other);
92    }
93 
operator std::string() const94    operator std::string() const
95    {
96       if (pieces_.empty())
97          return std::string();
98 
99       std::stringstream ss;
100       ss << boost::lexical_cast<std::string>(pieces_[0]);
101       for (std::size_t i = 1, n = pieces_.size(); i < n; ++i)
102       {
103          ss << ".";
104          ss << boost::lexical_cast<std::string>(pieces_[i]);
105       }
106 
107       return ss.str();
108    }
109 
110 private:
111 
compare(Version lhs,Version rhs) const112    int compare(Version lhs, Version rhs) const
113    {
114       std::size_t n = std::max(lhs.size(), rhs.size());
115 
116       for (std::size_t i = 0; i < n; ++i)
117       {
118          int vl = lhs.extractVersion(i);
119          int vr = rhs.extractVersion(i);
120          if (vl < vr)
121             return -1;
122          else if (vl > vr)
123             return 1;
124       }
125 
126       return 0;
127    }
128 
size() const129    std::size_t size() const { return pieces_.size(); }
130 
131    std::vector<int> pieces_;
132 };
133 
operator <(const Version & lhs,const Version & rhs)134 inline bool operator  <(const Version& lhs, const Version& rhs)
135 {
136    return lhs.compare(rhs) < 0;
137 }
138 
operator <=(const Version & lhs,const Version & rhs)139 inline bool operator <=(const Version& lhs, const Version& rhs)
140 {
141    return lhs.compare(rhs) <= 0;
142 }
143 
operator ==(const Version & lhs,const Version & rhs)144 inline bool operator ==(const Version& lhs, const Version& rhs)
145 {
146    return lhs.compare(rhs) == 0;
147 }
148 
operator !=(const Version & lhs,const Version & rhs)149 inline bool operator !=(const Version& lhs, const Version& rhs)
150 {
151    return lhs.compare(rhs) != 0;
152 }
153 
operator >=(const Version & lhs,const Version & rhs)154 inline bool operator >=(const Version& lhs, const Version& rhs)
155 {
156    return lhs.compare(rhs) >= 0;
157 }
158 
operator >(const Version & lhs,const Version & rhs)159 inline bool operator  >(const Version& lhs, const Version& rhs)
160 {
161    return lhs.compare(rhs) > 0;
162 }
163 
164 
165 } // end namespace core
166 } // end namespace rstudio
167 
168 #endif /* CORE_VERSION_HPP */
169