1 module toml_foolery.decode.types.integer;
2 
3 import std.algorithm : filter, all;
4 import std.ascii : isASCII, isHexDigit;
5 import std.conv;
6 
7 import toml_foolery.decode.exceptions;
8 
9 version(unittest) import dshould;
10 
11 
12 package(toml_foolery.decode) long parseTomlInteger(string value)
13 in (
14     value.all!(
15         (e) => (
16             (e.isASCII && (
17                 e.isHexDigit ||
18                 e == 'x' ||
19                 e == 'o' ||
20                 e == 'b' ||
21                 e == '-' ||
22                 e == '+'
23             )) || e == '_'
24         )
25     )
26 )
27 {
28     int radix = value.length < 3    ?  10  :
29                 value[0..2] == "0x" ?  16  :
30                 value[0..2] == "0o" ?  8   :
31                 value[0..2] == "0b" ?  2   :
32                 10;
33 
34     try
35     {
36         return (radix != 10 ? value[2..$] : value).filter!(e => e != '_').to!long(radix);
37     }
38     catch (ConvOverflowException e)
39     {
40         throw new TomlTypeException(
41             `Integer ` ~ value ~
42             ` is outside permitted range for TOML integers [-2^⁶³, 2^⁶³ - 1]`
43         );
44     }
45 }
46 
47 @("Positive")
48 unittest
49 {
50     parseTomlInteger("123").should.equal(123);
51 }
52 
53 @("Positive with leading +")
54 unittest
55 {
56     parseTomlInteger("+123").should.equal(123);
57 }
58 
59 @("Negative")
60 unittest
61 {
62     parseTomlInteger("-123").should.equal(-123);
63 }
64 
65 @("Zero")
66 unittest
67 {
68     parseTomlInteger("0").should.equal(0);
69 }
70 
71 @("Underscores — Positive")
72 unittest
73 {
74     parseTomlInteger("525_600").should.equal(525_600);
75 }
76 
77 @("Underscores — Negative")
78 unittest
79 {
80     parseTomlInteger("-189_912").should.equal(-189_912);
81 }
82 
83 @("Hex — lowercase")
84 unittest
85 {
86     parseTomlInteger("0xbee").should.equal(0xbee);
87 }
88 
89 @("Hex — uppercase")
90 unittest
91 {
92     parseTomlInteger("0xBEE").should.equal(0xbee);
93 }
94 
95 @("Hex — mixed case")
96 unittest
97 {
98     parseTomlInteger("0xbEe").should.equal(0xbee);
99 }
100 
101 @("Hex — long")
102 unittest
103 {
104     parseTomlInteger("0xbeadface").should.equal(0xBeadFace);
105 }
106 
107 @("Hex — underscores")
108 unittest
109 {
110     parseTomlInteger("0xb_e_e").should.equal(0xbee);
111 }
112 
113 @("Octal")
114 unittest
115 {
116     parseTomlInteger("0o777").should.equal(511);
117 }
118 
119 @("Binary")
120 unittest
121 {
122     parseTomlInteger("0b11001101").should.equal(205);
123 }