1 /**
2     Functions that transform strings into values or vice versa.
3 
4     Copyright: © 2019 Arne Ludwig <arne.ludwig@posteo.de>
5     License: Subject to the terms of the MIT license, as written in the
6              included LICENSE file.
7     Authors: Arne Ludwig <arne.ludwig@posteo.de>
8 */
9 module darg_plus..string;
10 
11 
12 /**
13     Parses a range in form `x..y` into a 2-element array.
14 
15     Params:
16         dest        = Range limits are written to the aliased static array.
17         DestType    = Allocate and return a static array of this type.
18         msg         = Error message in case of failure.
19         rangeString = The string to be parsed. The expected format is `x..y`.
20 */
21 void parseRange(alias dest, string msg = "ill-formatted range")(in string rangeString) pure
22         if (isStaticArray!(typeof(dest)) && dest.length == 2)
23 {
24     try
25     {
26         rangeString[].formattedRead!"%d..%d"(dest[0], dest[1]);
27     }
28     catch (Exception e)
29     {
30         throw new CLIException(msg);
31     }
32 }
33 
34 /// ditto
35 DestType parseRange(DestType, string msg = "ill-formatted range")(in string rangeString) pure
36         if (isStaticArray!DestType && DestType.init.length == 2)
37 {
38     try
39     {
40         DestType dest;
41 
42         rangeString[].formattedRead!"%d..%d"(dest[0], dest[1]);
43 
44         return dest;
45     }
46     catch (Exception e)
47     {
48         throw new CLIException(msg);
49     }
50 }
51 
52 import std.traits : isFloatingPoint;
53 
54 /**
55     Convert a floating point number to a base-10 string at compile time.
56     This function is very crude and will not work in many cases!
57 */
58 string toString(Float)(in Float value, in uint precision) pure nothrow
59     if (isFloatingPoint!Float)
60 {
61     import std.conv : to;
62     import std.math :
63         ceil,
64         floor,
65         isInfinity,
66         isNaN,
67         round,
68         sgn;
69 
70     if (value.isNaN)
71         return "nan";
72     else if (value.isInfinity)
73         return value > 0 ? "inf" : "-inf";
74 
75     if (precision == 0)
76     {
77         auto intPart = cast(long) round(value);
78 
79         return intPart.to!string;
80     }
81     else
82     {
83         auto intPart = cast(long) (value > 0 ? floor(value) : ceil(value));
84         auto fraction = sgn(value) * (value - intPart);
85         assert(fraction >= 0, "fractional part of value should be non-negative");
86         auto fracPart = cast(ulong) round(10^^precision * fraction);
87 
88         return intPart.to!string ~ "." ~ fracPart.to!string;
89     }
90 }
91 
92 ///
93 unittest
94 {
95     enum x = 42.0;
96     enum y = -13.37f;
97     enum z = 0.9;
98 
99     static assert(float.nan.toString(0) == "nan");
100     static assert(double.infinity.toString(0) == "inf");
101     static assert((-double.infinity).toString(0) == "-inf");
102     static assert(x.toString(0) == "42");
103     static assert(x.toString(1) == "42.0");
104     static assert(y.toString(2) == "-13.37");
105     static assert(y.toString(1) == "-13.4");
106     static assert(y.toString(0) == "-13");
107     static assert(z.toString(1) == "0.9");
108     static assert(z.toString(0) == "1");
109 }