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 exceeds_expectations;
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     expect(parseTomlInteger("123")).toEqual(123);
51 }
52 
53 @("Positive with leading +")
54 unittest
55 {
56     expect(parseTomlInteger("+123")).toEqual(123);
57 }
58 
59 @("Negative")
60 unittest
61 {
62     expect(parseTomlInteger("-123")).toEqual(-123);
63 }
64 
65 @("Zero")
66 unittest
67 {
68     expect(parseTomlInteger("0")).toEqual(0);
69 }
70 
71 @("Underscores — Positive")
72 unittest
73 {
74     expect(parseTomlInteger("525_600")).toEqual(525_600);
75 }
76 
77 @("Underscores — Negative")
78 unittest
79 {
80     expect(parseTomlInteger("-189_912")).toEqual(-189_912);
81 }
82 
83 @("Hex — lowercase")
84 unittest
85 {
86     expect(parseTomlInteger("0xbee")).toEqual(0xbee);
87 }
88 
89 @("Hex — uppercase")
90 unittest
91 {
92     expect(parseTomlInteger("0xBEE")).toEqual(0xbee);
93 }
94 
95 @("Hex — mixed case")
96 unittest
97 {
98     expect(parseTomlInteger("0xbEe")).toEqual(0xbee);
99 }
100 
101 @("Hex — long")
102 unittest
103 {
104     expect(parseTomlInteger("0xbeadface")).toEqual(0xBeadFace);
105 }
106 
107 @("Hex — underscores")
108 unittest
109 {
110     expect(parseTomlInteger("0xb_e_e")).toEqual(0xbee);
111 }
112 
113 @("Octal")
114 unittest
115 {
116     expect(parseTomlInteger("0o777")).toEqual(511);
117 }
118 
119 @("Binary")
120 unittest
121 {
122     expect(parseTomlInteger("0b11001101")).toEqual(205);
123 }