1 /** 2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 A type that holds a semantic version that provide ordering and easy 7 construction from a plain `string`. 8 */ 9 module my.semver; 10 11 @safe: 12 13 /** A parsed semantic version. 14 * 15 * Currently only supports the numbering, no metadata after a `+`-sign. 16 */ 17 struct SemVer { 18 import std.range : isOutputRange; 19 20 private int[3] value_; 21 22 /// Returns: major version number (first number). 23 int major() pure nothrow const @nogc { 24 return value_[0]; 25 } 26 27 /// Returns: minor version number (second number). 28 int minor() pure nothrow const @nogc { 29 return value_[1]; 30 } 31 32 /// Returns: bugfix version number (third number). 33 int bugFix() pure nothrow const @nogc { 34 return value_[2]; 35 } 36 37 /// 38 int opCmp(const SemVer rhs) pure nothrow const @nogc { 39 foreach (i; 0 .. value_.length) { 40 if (value_[i] < rhs.value_[i]) 41 return -1; 42 if (value_[i] > rhs.value_[i]) 43 return 1; 44 } 45 return 0; 46 } 47 48 /// 49 bool opEquals(const SemVer x) pure nothrow const @nogc { 50 return value_ == x.value_; 51 } 52 53 /// 54 bool opEquals(const int[] x) pure nothrow const @nogc { 55 return value_[] == x; 56 } 57 58 /// 59 string toString() @safe pure const { 60 import std.array : appender; 61 62 auto buf = appender!string; 63 toString(buf); 64 return buf.data; 65 } 66 67 /// 68 void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) { 69 import std.format : formattedWrite; 70 71 formattedWrite(w, "%s.%s.%s", value_[0], value_[1], value_[2]); 72 } 73 } 74 75 /** Convert a string to a `SemVer`. 76 * 77 * An invalid `string` is returned as a `SemVer` with version `0.0.0`. 78 */ 79 SemVer toSemVer(string s) { 80 import std.algorithm : filter; 81 import std.array : empty; 82 import std.conv : to; 83 import std.range : dropOne, enumerate; 84 import std.regex : regex, matchFirst; 85 86 SemVer rval; 87 88 const re = regex(`^(?:(\d+)\.)?(?:(\d+)\.)?(\d+)$`); 89 auto m = matchFirst(s, re); 90 if (m.empty) 91 return rval; 92 93 try { 94 foreach (a; m.dropOne.filter!(a => !a.empty).enumerate) { 95 rval.value_[a.index] = a.value.to!int; 96 } 97 } catch (Exception e) { 98 } 99 100 return rval; 101 } 102 103 /// Example. 104 unittest { 105 import std.stdio : writeln; 106 107 assert(toSemVer("1.2.3") == [1, 2, 3]); 108 assert(toSemVer("1.2") == [1, 2, 0]); 109 assert(toSemVer("1") == [1, 0, 0]); 110 assert(toSemVer("1.2.3.4") == [0, 0, 0]); 111 }