1 module m3.Transform;
2 
3 private:
4 
5 static import core.stdc.stdlib;
6 alias strtol = core.stdc.stdlib.strtol;
7 alias strtoul = core.stdc.stdlib.strtoul;
8 alias strtof = core.stdc.stdlib.strtof;
9 alias strtod = core.stdc.stdlib.strtod;
10 
11 static import core.stdc.stdio;
12 alias sprintf = core.stdc.stdio.sprintf;
13 
14 static import core.stdc.string;
15 alias strlen = core.stdc..string.strlen;
16 alias strtok = core.stdc..string.strtok;
17 
18 static import std.traits;
19 alias isNumeric = std.traits.isNumeric;
20 alias isBoolean = std.traits.isBoolean;
21 alias isSomeString = std.traits.isSomeString;
22 alias isSomeChar = std.traits.isSomeChar;
23 
24 static import m3.m3;
25 
26 public:
27 
28 @nogc
29 T convert(T)(string str) nothrow if (isNumeric!(T) || isBoolean!(T)) {
30     static if (is(T : ulong)) {
31         immutable ulong value = strtoul(str.ptr, null, 0);
32         static if (is(T == ulong))
33             return value;
34         else
35             return cast(T) value;
36     } else static if (is(T : long)) {
37         immutable long value = strtol(str.ptr, null, 0); 
38         static if (is(T == long))
39             return value;
40         else
41             return cast(T) value;
42     } else static if (is(T == float))
43         return strtof(str.ptr, null);
44     else static if (is(T : real)) {
45         immutable real value = strtod(str.ptr, null);
46         static if (is(T == real))
47             return value;
48         else
49             return cast(T) value;
50     } else static if (is(T == bool))
51         return strtol(str.ptr, null, 0);
52     else
53         static assert(0);
54 }
55 
56 @nogc
57 string convert(T)(T value) nothrow if (isNumeric!(T) || isBoolean!(T)) {
58     static char[16] buf;
59 
60     static if (is(T : ulong))
61         sprintf(buf.ptr, "%u", value);
62     else static if (is(T : long))
63         sprintf(buf.ptr, "%d", value);
64     else static if (is(T : real))
65         sprintf(buf.ptr, "%f", value);
66     else static if (is(T == bool))
67         sprintf(buf.ptr, "%d", value);
68     else
69         static assert(0);
70 
71     return cast(immutable) buf[0 .. strlen(buf.ptr)];
72 }
73 
74 @nogc
75 string format(size_t SIZE = 256, Args...)(string format, auto ref Args args) nothrow {
76     static assert(SIZE >= (Args.length * 16));
77 
78     static char[SIZE] buf = void;
79 
80     size_t i = 0, j = 0;
81     foreach (immutable size_t ai, arg; args) {
82         for (; j < format.length; j++) {
83             if (format[j] == '{') {
84                 if ((j + 1) < format.length && format[j + 1] == '}') {
85                     static if (isSomeString!(Args[ai]))
86                         immutable string s = arg;
87                     else static if (isSomeChar!(Args[ai])) {
88                         const char[1] str = arg;
89                         immutable string s = cast(immutable) str;
90                     } else static if (is(Args[ai] == class))
91                         immutable string s = arg ? arg.toString() : null.stringof;
92                     else static if (is(Args[ai] == struct))
93                         immutable string s = arg.toString();
94                     else static if (is(Args[ai] : U[], U))
95                         static assert(0, "Arrays cannot be formated.");
96                     else
97                         immutable string s = convert(arg);
98 
99                     buf[i .. i + s.length] = s;
100 
101                     i += s.length;
102                     j += 2;
103 
104                     break;
105                 }
106             }
107 
108             buf[i] = format[j];
109             i++;
110         }
111     }
112 
113     if (j < format.length) {
114         immutable size_t r = format.length - j;
115         buf[i .. i + r] = format[j .. $];
116 
117         i += r;
118     }
119 
120     return cast(immutable) buf[0 .. i];
121 }
122 
123 @nogc
124 string[] split(string str, char delim) nothrow {
125     size_t count = 0;
126     foreach (char c; str) {
127         if (c == delim)
128             count++;
129     }
130 
131     if (count == 0)
132         return null;
133 
134     count++;
135 
136     string[] result = m3.m3.make!(string[])(count);
137     size_t i = 0;
138 
139     char* p = strtok(cast(char*) str.ptr, &delim);
140     result[i++] = cast(immutable) p[0 .. strlen(p)];
141 
142     while (count > i) {
143         p = strtok(null, &delim);
144         if (p)
145             result[i++] = cast(immutable) p[0 .. strlen(p)];
146         else
147             break;
148     }
149 
150     return result;
151 }
152 
153 version (unittest) {
154     class A {
155         @nogc
156         override string toString() const pure nothrow {
157             return "A";
158         }
159     }
160 
161     struct B {
162         @nogc
163         string toString() const pure nothrow {
164             return "B";
165         }
166     }
167 }
168 
169 @nogc
170 unittest {
171     // Convert to
172 
173     assert(convert!(int)("152") == 152);
174     assert(convert!(uint)("152") == 152);
175 
176     assert(convert!(byte)("152"));
177     assert(convert!(ubyte)("152") == 152);
178 
179     assert(convert!(short)("152") == 152);
180     assert(convert!(ushort)("152") == 152);
181 
182     assert(convert!(long)("152") == 152);
183     assert(convert!(ulong)("152") == 152);
184 
185     assert(convert!(int)("152.52") == 152);
186 
187     assert(convert!(float)("152.52") is 152.52f);
188     assert(convert!(double)("152.52") == 152.52);
189     //assert(convert!(real)("152.52") == real(152.52));
190 
191     // Convert from
192 
193     assert(convert(152) == "152");
194     assert(convert(152.52f) == "152.520004");
195     assert(convert(152.52) == "152.520000");
196     assert(convert(false) == "0");
197     assert(convert(true) == "1");
198 
199     // Format
200 
201     assert(format("test_{}.png", 1) == "test_1.png");
202     assert(format("test_{}.png", '0') == "test_0.png");
203     assert(format("{} + {} = {}", 42, 23, 42 + 23) == "42 + 23 = 65");
204     assert(format("Erst kommt die {}, dann die {} und am Ende die {}. Nicht zu vergessen die {}, die kommt vor {}.", 11, 12, 42, 23, 42) ==
205         "Erst kommt die 11, dann die 12 und am Ende die 42. Nicht zu vergessen die 23, die kommt vor 42.");
206     assert(format("Hallo {}, ich bin {} und {} Jahre alt.", "Foo", "Bar", 42) == "Hallo Foo, ich bin Bar und 42 Jahre alt.");
207 
208     const int[2] iarr = [1, 2];
209     assert(!__traits(compiles, { format("Foo {} Bar", iarr); }));
210 
211     A a;
212     B b;
213 
214     assert(format(" A : {}, B = {}", a, b) == " A : null, B = B");
215 
216     // split
217 
218     string str = "Foo:Bar:Quatz";
219     auto result = str.split(':');
220 
221     assert(result.length == 3);
222     assert(result[0] == "Foo");
223     assert(result[1] == "Bar");
224     assert(result[2] == "Quatz");
225 
226     m3.m3.destruct(result);
227 }