1 /++ Conversion that use result types instead of exception handling. 2 +/ 3 module nxt.conv; 4 5 import nxt.result : Result; 6 7 @safe: 8 9 /++ Try to parse `s` as a `T`. +/ 10 Result!(T) tryParse(T)(scope const(char)[] s) pure nothrow @nogc 11 if (__traits(isArithmetic, T) && __traits(isIntegral, T)) { 12 alias R = typeof(return); 13 14 // accumulator type 15 static if (__traits(isUnsigned, T)) 16 alias A = ulong; 17 else 18 alias A = long; 19 20 if (!s.length) // empty 21 return R.invalid; 22 23 // strip optional leading {plus|minus} sign 24 bool minus; 25 if (s.length) { 26 if (s[0] == '-') 27 minus = true; 28 if (s[0] == '+' || s[0] == '-') 29 s = s[1 .. $]; 30 } 31 32 // accumulate 33 A curr = 0; // current accumulated value 34 foreach (const c; s) { 35 if (c < '0' || c > '9') // non-digit 36 return R.invalid; 37 A prev = curr; 38 curr = 10 * curr + (c - '0'); // accumulate 39 if (curr < prev) // overflow 40 return R.invalid; 41 } 42 43 // range check 44 assert(T.min <= curr); 45 assert(curr <= T.max); 46 47 return R(cast(T)curr); 48 } 49 50 @safe pure nothrow @nogc unittest { 51 foreach (T; IntegralTypes) { 52 assert(!"".tryParse!T.isValid); // empty 53 assert(!"_".tryParse!T.isValid); // non-digit 54 assert(*"+0".tryParse!T == 0); 55 assert(!*"+0".tryParse!T); 56 assert(*"+1".tryParse!T == 1); 57 assert(*"-0".tryParse!T == 0); 58 assert(*"0".tryParse!T == 0); 59 assert(*"1".tryParse!T == 1); 60 assert(*"2".tryParse!T == 2); 61 assert(*"10".tryParse!T == 10); 62 assert(*"11".tryParse!T == 11); 63 } 64 // unsigned min 65 foreach (T; UnsignedTypes) { 66 assert(*"0".tryParse!T == T.min); 67 assert(*"+0".tryParse!T == T.min); 68 } 69 // unsigned max 70 assert(*"255".tryParse!ubyte == 255); 71 assert(*"65535".tryParse!ushort == 65535); 72 assert(*"4294967295".tryParse!uint == 4294967295); 73 assert(*"18446744073709551615".tryParse!ulong == ulong.max); 74 // signed max 75 assert(*"9223372036854775807".tryParse!long == long.max); 76 // overflow 77 assert(!"18446744073709551616".tryParse!ulong); 78 } 79 80 version (unittest) { 81 import std.meta : AliasSeq; 82 alias UnsignedTypes = AliasSeq!(ubyte, ushort, uint, ulong); 83 alias SignedTypes = AliasSeq!(byte, short, int, long); 84 alias IntegralTypes = AliasSeq!(ubyte, ushort, uint, ulong, byte, short, int, long); 85 }