1 /**
2 * @file svnxx/revision.hpp
3 * @copyright
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 * @endcopyright
23 */
24
25 #ifndef SVNXX_REVISION_HPP
26 #define SVNXX_REVISION_HPP
27
28 #include "svn_opt_impl.h"
29 #include "svn_types_impl.h"
30
31 #include <chrono>
32 #include <cstdint>
33 #include <new>
34
35 #include "tristate.hpp"
36
37 namespace apache {
38 namespace subversion {
39 namespace svnxx {
40
41 /**
42 * @brief A revision, see @ref svn_opt_revision_t.
43 *
44 * The @c revision can represent a revision number, a point in time
45 * in the repository or a property of the working copy or repository
46 * node (see revision::kind).
47 */
48 class revision
49 {
50 public:
51 /**
52 * @brief Revision number type.
53 */
54 enum class number : svn_revnum_t
55 {
56 invalid = SVN_INVALID_REVNUM, ///< Invalid revision number.
57 };
58
59 /**
60 * @brief Revision by date/time uses the system clock.
61 */
62 template<typename Duration>
63 using time = std::chrono::time_point<std::chrono::system_clock, Duration>;
64
65 /**
66 * @brief The resolution of the stored date/time.
67 */
68 using usec = std::chrono::microseconds;
69
70 /**
71 * @brief Revision kind discriminator (see @ref svn_opt_revision_kind).
72 */
73 // NOTE: Keep these values identical to those in svn_opt_revision_kind!
74 enum class kind : std::int8_t
75 {
76 unspecified = svn_opt_revision_unspecified,
77 number = svn_opt_revision_number,
78 date = svn_opt_revision_date,
79 committed = svn_opt_revision_committed,
80 previous = svn_opt_revision_previous,
81 base = svn_opt_revision_base,
82 working = svn_opt_revision_working,
83 head = svn_opt_revision_head,
84 };
85
86 /**
87 * @brief Default constructor.
88 * @post get_kind() == kind::unspecified.
89 */
revision()90 revision() noexcept
91 : tag(kind::unspecified)
92 {}
93
94 /**
95 * @brief Construct a revision of the given kind.
96 * @pre The @a revkind argument may be any @c kind value @b except
97 * kind::number or kind::date, which require additional
98 * parameters and therefore have their own constructors.
99 * @post get_kind() == @a revkind.
100 * @throw std::invalid_argument if the @a revkind value
101 * precondition is not met.
102 */
revision(kind revkind)103 explicit revision(kind revkind)
104 : tag(revkind)
105 {
106 if (revkind == kind::number || revkind == kind::date)
107 throw std::invalid_argument("invalid svn::revision::kind");
108 }
109
110 /**
111 * @brief Construct a numbered revision.
112 * @post get_kind() == kind::number.
113 */
revision(number revnum_)114 explicit revision(number revnum_) noexcept
115 : tag(kind::number),
116 revnum(revnum_)
117 {}
118
119 /**
120 * @brief Construct a dated revision from a system clock time point.
121 * @post get_kind() == kind::date.
122 */
123 template<typename D>
revision(time<D> time_)124 explicit revision(time<D> time_) noexcept
125 : tag(kind::date),
126 date(std::chrono::time_point_cast<usec>(time_))
127 {}
128
129 /**
130 * @brief Assignment operator.
131 * Uses in-place destruction/construction to maintain the immutability
132 * of the revision kind.
133 */
operator =(const revision & that)134 revision& operator=(const revision& that)
135 {
136 this->~revision();
137 new(this) revision(that);
138 return *this;
139 }
140
141 /**
142 * @brief Return the revision kind.
143 */
get_kind() const144 kind get_kind() const noexcept
145 {
146 return tag;
147 }
148
149 /**
150 * @brief Return the revision number.
151 * @pre get_kind() == kind::number.
152 * @throw std::logic_error if the precondition is not met.
153 */
get_number() const154 number get_number() const
155 {
156 if (tag != kind::number)
157 throw std::logic_error("svn::revision kind != number");
158 return revnum;
159 }
160
161 /**
162 * @brief Return the revision date/time as a system clock time point.
163 * @pre get_kind() == kind::date.
164 * @throw std::logic_error if the precondition is not met.
165 */
166 template<typename D>
get_date() const167 time<D> get_date() const
168 {
169 if (tag != kind::date)
170 throw std::logic_error("svn::revision kind != date");
171 return std::chrono::time_point_cast<D>(date);
172 }
173
174 private:
175 // Even if we were using C++17, we wouldn't use std::variant because we
176 // already maintain an explicit discriminator tag for the union.
177 const kind tag; // Union discriminator
178 union {
179 number revnum; // (tag == kind::number): revision number.
180 time<usec> date; // (tag == kind::date): microseconds from epoch.
181 };
182 };
183
184 /**
185 * @related revision
186 * @brief revision::number alias for convenience.
187 */
188 using revnum = revision::number;
189
190 /**
191 * @related revision
192 * @brief Equality comparison.
193 */
operator ==(const revision & a,const revision & b)194 inline bool operator==(const revision& a, const revision& b)
195 {
196 const auto kind = a.get_kind();
197 if (kind != b.get_kind())
198 return false;
199 else if (kind == revision::kind::number)
200 return a.get_number() == b.get_number();
201 else if (kind == revision::kind::date)
202 return a.get_date<revision::usec>() == b.get_date<revision::usec>();
203 else
204 return true;
205 }
206
207 /**
208 * @related revision
209 * @brief Inequality comparison.
210 */
operator !=(const revision & a,const revision & b)211 inline bool operator!=(const revision& a, const revision& b)
212 {
213 return !(a == b);
214 }
215
216 /**
217 * @related revision
218 * @brief Ordering: less-than (<tt>operator @<</tt>).
219 * @returns a @c tristate result of comparing two @c revision values,
220 * according to the following table:
221 * <table border=1>
222 * <tr>
223 * <th><center><code>@<</code></center></th>
224 * <th><center><tt>number</tt></center></th>
225 * <th><center><tt>date</tt></center></th>
226 * <th><center><em>other</em></center></th>
227 * </tr>
228 * <tr>
229 * <th><center><tt>number</tt></center></th>
230 * <td><center><tt>a.get_number() < b.get_number()</tt></center></td>
231 * <td><center><em>unknown</em></center></td>
232 * <td><center><em>unknown</em></center></td>
233 * </tr>
234 * <tr>
235 * <th><center><tt>date</tt></center></th>
236 * <td><center><em>unknown</em></center></td>
237 * <td><center><tt>a.get_date() < b.get_date()</tt></center></td>
238 * <td><center><em>unknown</em></center></td>
239 * </tr>
240 * <tr>
241 * <th><center><em>other</em></center></th>
242 * <td><center><em>unknown</em></center></td>
243 * <td><center><em>unknown</em></center></td>
244 * <td><center><em>unknown</em></center></td>
245 * </tr>
246 * </table>
247 */
operator <(const revision & a,const revision & b)248 inline tristate operator<(const revision& a, const revision& b)
249 {
250 const auto kind = a.get_kind();
251 if (kind != b.get_kind())
252 return tristate::unknown();
253 else if (kind == revision::kind::number)
254 return a.get_number() < b.get_number();
255 else if (kind == revision::kind::date)
256 return a.get_date<revision::usec>() < b.get_date<revision::usec>();
257 else
258 return tristate::unknown();
259 }
260
261 /**
262 * @related revision
263 * @brief Ordering: greater-than (<tt>operator @></tt>).
264 * @returns a @c tristate result of comparing two @c revision values,
265 * according to the following table:
266 * <table border=1>
267 * <tr>
268 * <th><center><code>@></code></center></th>
269 * <th><center><tt>number</tt></center></th>
270 * <th><center><tt>date</tt></center></th>
271 * <th><center><em>other</em></center></th>
272 * </tr>
273 * <tr>
274 * <th><center><tt>number</tt></center></th>
275 * <td><center><tt>a.get_number() > b.get_number()</tt></center></td>
276 * <td><center><em>unknown</em></center></td>
277 * <td><center><em>unknown</em></center></td>
278 * </tr>
279 * <tr>
280 * <th><center><tt>date</tt></center></th>
281 * <td><center><em>unknown</em></center></td>
282 * <td><center><tt>a.get_date() > b.get_date()</tt></center></td>
283 * <td><center><em>unknown</em></center></td>
284 * </tr>
285 * <tr>
286 * <th><center><em>other</em></center></th>
287 * <td><center><em>unknown</em></center></td>
288 * <td><center><em>unknown</em></center></td>
289 * <td><center><em>unknown</em></center></td>
290 * </tr>
291 * </table>
292 */
operator >(const revision & a,const revision & b)293 inline tristate operator>(const revision& a, const revision& b)
294 {
295 const auto kind = a.get_kind();
296 if (kind != b.get_kind())
297 return tristate::unknown();
298 else if (kind == revision::kind::number)
299 return a.get_number() > b.get_number();
300 else if (kind == revision::kind::date)
301 return a.get_date<revision::usec>() > b.get_date<revision::usec>();
302 else
303 return tristate::unknown();
304 }
305
306 /**
307 * @related revision
308 * @brief Ordering: less-or-equal (<tt>operator @<=</tt>).
309 * @returns a @c tristate result of comparing two @c revision values,
310 * according to the following table:
311 * <table border=1>
312 * <tr>
313 * <th><center><code>@<=</code></center></th>
314 * <th><center><tt>number</tt></center></th>
315 * <th><center><tt>date</tt></center></th>
316 * <th><center><em>other</em></center></th>
317 * </tr>
318 * <tr>
319 * <th><center><tt>number</tt></center></th>
320 * <td><center><tt>a.get_number() <= b.get_number()</tt></center></td>
321 * <td><center><em>unknown</em></center></td>
322 * <td><center><em>unknown</em></center></td>
323 * </tr>
324 * <tr>
325 * <th><center><tt>date</tt></center></th>
326 * <td><center><em>unknown</em></center></td>
327 * <td><center><tt>a.get_date() <= b.get_date()</tt></center></td>
328 * <td><center><em>unknown</em></center></td>
329 * </tr>
330 * <tr>
331 * <th><center><em>other</em></center></th>
332 * <td><center><em>unknown</em></center></td>
333 * <td><center><em>unknown</em></center></td>
334 * <td><center><em>true</em>† or <em>unknown</em></center></td>
335 * </tr>
336 * </table>
337 * † <em>true</em> when <tt>a.get_kind() == b.get_kind()</tt>.
338 */
operator <=(const revision & a,const revision & b)339 inline tristate operator<=(const revision& a, const revision& b)
340 {
341 return (a == b || !(a > b));
342 }
343
344 /**
345 * @related revision
346 * @brief Ordering: greater-or-equal (<tt>operator @>=</tt>).
347 * @returns a @c tristate result of comparing two @c revision values,
348 * according to the following table:
349 * <table border=1>
350 * <tr>
351 * <th><center><code>@>=</code></center></th>
352 * <th><center><tt>number</tt></center></th>
353 * <th><center><tt>date</tt></center></th>
354 * <th><center><em>other</em></center></th>
355 * </tr>
356 * <tr>
357 * <th><center><tt>number</tt></center></th>
358 * <td><center><tt>a.get_number() >= b.get_number()</tt></center></td>
359 * <td><center><em>unknown</em></center></td>
360 * <td><center><em>unknown</em></center></td>
361 * </tr>
362 * <tr>
363 * <th><center><tt>date</tt></center></th>
364 * <td><center><em>unknown</em></center></td>
365 * <td><center><tt>a.get_date() >= b.get_date()</tt></center></td>
366 * <td><center><em>unknown</em></center></td>
367 * </tr>
368 * <tr>
369 * <th><center><em>other</em></center></th>
370 * <td><center><em>unknown</em></center></td>
371 * <td><center><em>unknown</em></center></td>
372 * <td><center><em>true</em>† or <em>unknown</em></center></td>
373 * </tr>
374 * </table>
375 * † <em>true</em> when <tt>a.get_kind() == b.get_kind()</tt>.
376 */
operator >=(const revision & a,const revision & b)377 inline tristate operator>=(const revision& a, const revision& b)
378 {
379 return (a == b || !(a < b));
380 }
381
382 } // namespace svnxx
383 } // namespace subversion
384 } // namespace apache
385
386 #endif // SVNXX_REVISION_HPP
387