1 module bc..string;
2 
3 public import bc..string.format;
4 public import bc..string.numeric;
5 public import bc..string.string;
6 
7 import core.stdc.stdio;
8 
9 version (CI_MAIN)
10 {
11     // workaround for dub not supporting unittests with betterC
12     version (D_BetterC)
13     {
14         extern(C) int main()
15         {
16             version (unittest)
17             {
18                 import std.meta : AliasSeq;
19 
20                 alias modules = AliasSeq!(bc..string.format, bc..string.numeric, bc..string.string);
21                 static foreach (m; modules)
22                 {
23                     static foreach(u; __traits(getUnitTests, m)) {
24                         debug printf("testing " ~ m.stringof ~ ": '" ~ __traits(getAttributes, u)[0] ~ "'\n");
25                         u();
26                     }
27                 }
28                 debug printf("All unit tests have been run successfully.\n");
29                 return 0;
30             }
31             else return rtTest();
32         }
33     }
34     else
35     {
36         int main()
37         {
38             version (unittest) return 0; // run automagically
39             else return rtTest();
40         }
41     }
42 
43     int rtTest()()
44     {
45         // just a compilation test - same as in the README to be sure it actually works..
46 
47         import bc.core.memory;
48         import bc.internal.utf : byCodeUnit;
49         import core.stdc..string : strlen;
50         import std.algorithm : filter;
51         import std.range : chunks;
52 
53         assert(nogcFormat!"abcd abcd" == "abcd abcd");
54         assert(nogcFormat!"123456789a" == "123456789a");
55         version (D_NoBoundsChecks) {}
56         else version (D_BetterC) {}
57         else
58         {
59             () @trusted
60             {
61                 import core.exception : RangeError;
62                 import std.exception : assertThrown;
63                 char[5] buf;
64                 version (assert) assertThrown!RangeError(buf.nogcFormatTo!"123412341234");
65             }();
66         }
67 
68         // literal escape
69         assert(nogcFormat!"123 %%" == "123 %");
70         assert(nogcFormat!"%%%%" == "%%");
71 
72         // %d
73         assert(nogcFormat!"%d"(1234) == "1234");
74         assert(nogcFormat!"%4d"(42) == "  42");
75         assert(nogcFormat!"%04d"(42) == "0042");
76         assert(nogcFormat!"%04d"(-42) == "-042");
77         assert(nogcFormat!"ab%dcd"(1234) == "ab1234cd");
78         assert(nogcFormat!"ab%d%d"(1234, 56) == "ab123456");
79 
80         // %x
81         assert(nogcFormat!"0x%x"(0x1234) == "0x1234");
82 
83         // %p
84         assert(nogcFormat!("%p")(0x1234) == "0000000000001234");
85 
86         // %s
87         assert(nogcFormat!"12345%s"("12345") == "1234512345");
88         assert(nogcFormat!"12345%s"(12345) == "1234512345");
89         enum Floop {XXX, YYY, ZZZ}
90         assert(nogcFormat!"12345%s"(Floop.YYY) == "12345YYY");
91         char[4] str = "foo\0";
92         assert(nogcFormat!"%s"(&str[0]) == "foo");
93 
94         version (D_BetterC) {} // can't import std.uuid in betterC
95         else
96         {
97             import std.uuid;
98             assert(nogcFormat!"%s"(
99                 parseUUID("22390768-cced-325f-8f0f-cfeaa19d0ccd"))
100                 == "22390768-cced-325f-8f0f-cfeaa19d0ccd");
101         }
102 
103         // array format
104         version (D_BetterC)
105         {
106             int[] arr = (cast(int*)enforceMalloc(int.sizeof*10))[0..10];
107             foreach (i; 0..10) arr[i] = i;
108             scope (exit) pureFree(arr.ptr);
109         }
110         else auto arr = [0,1,2,3,4,5,6,7,8,9];
111 
112         assert(nogcFormat!"foo %(%d %)"(arr[1..4]) == "foo 1 2 3");
113         assert(nogcFormat!"foo %-(%d %)"(arr[1..4]) == "foo 1 2 3");
114         assert(nogcFormat!"foo %(-%d-%|, %)"(arr[1..4]) == "foo -1-, -2-, -3-");
115         assert(nogcFormat!"%(0x%02x %)"(arr[1..4]) == "0x01 0x02 0x03");
116         assert(nogcFormat!"%(%(%d %)\n%)"(arr[1..$].chunks(3)) == "1 2 3\n4 5 6\n7 8 9");
117 
118         // range format
119         auto r = arr.filter!(a => a < 5);
120         assert(nogcFormat!"%s"(r) == "[0, 1, 2, 3, 4]");
121 
122         // Arg num
123         assert(!__traits(compiles, nogcFormat!"abc"(5)));
124         assert(!__traits(compiles, nogcFormat!"%d"()));
125         assert(!__traits(compiles, nogcFormat!"%d a %d"(5)));
126 
127         // Format error
128         assert(!__traits(compiles, nogcFormat!"%"()));
129         assert(!__traits(compiles, nogcFormat!"abcd%d %"(15)));
130         assert(!__traits(compiles, nogcFormat!"%$"(1)));
131         assert(!__traits(compiles, nogcFormat!"%d"("hello")));
132         assert(!__traits(compiles, nogcFormat!"%x"("hello")));
133 
134         assert(nogcFormat!"Hello %s"(5) == "Hello 5");
135 
136         struct Foo { int x, y; }
137         assert(nogcFormat!("Hello %s")(Foo(1, 2)) == "Hello Foo(x=1, y=2)");
138 
139         version (D_BetterC)
140         {
141             struct Nullable(T) // can't be instanciated in betterC - fake just for the UT
142             {
143                 T get() { return T.init; }
144                 bool isNull() { return true; }
145                 void nullify() {}
146             }
147         }
148         else import std.typecons : Nullable;
149         struct Msg { Nullable!string foo; }
150         assert(nogcFormat!"%s"(Msg.init) == "Msg(foo=null)");
151 
152         RCString s = "abcd";
153         assert(nogcFormat!"%s"(s) == "abcd");
154 
155         version (D_BetterC) {}
156         else version (linux)
157         {
158             import bc.core.system.backtrace : TraceInfo;
159             printf("%s\n", nogcFormat!"where am i: %s"(TraceInfo.current).ptr);
160         }
161 
162         assert(numDigits(0) == 1);
163         assert(numDigits(11) == 2);
164         assert(numDigits(-1) == 2);
165 
166         assert(numDigits(int.min) == 11);
167         assert(numDigits(int.max) == 10);
168         assert(numDigits(long.min) == 20);
169         assert(numDigits(long.max) == 19);
170         assert(numDigits(ulong.min) == 1);
171         assert(numDigits(ulong.max) == 20);
172 
173         string str2 = "abc";
174 
175         // Intended usage
176         assert(strlen(str2.tempCString()) == 3);
177 
178         // Correct usage
179         auto tmp = str2.tempCString();
180         assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr`
181 
182         // Incorrect usage
183         auto pInvalid1 = str2.tempCString().ptr;
184         const char* pInvalid2 = str2.tempCString();
185 
186         RCString rcs;
187         rcs ~= "fo";
188         rcs ~= 'o';
189         rcs ~= "bar";
190         rcs ~= "baz".byCodeUnit.filter!(a => a == 'z');
191         assert(rcs.length == "foobarz".length);
192         assert(rcs.data == "foobarz");
193         assert(rcs == "foobarz");
194         assert(rcs.ptr == &rcs.data[0]);
195         assert((rcs.ptr["foobarz".length]) == 0);
196 
197         // construction from write like params
198         rcs = RCString.from("foo", 42, "bar");
199         assert(rcs == "foo42bar");
200 
201         auto ss = String("Hello");
202         assert(ss[] == "Hello", s[]);
203         ss ~= " String";
204         assert(ss[] == "Hello String", ss[]);
205         auto ss2 = ss.clone();
206         assert(ss[] == ss2[]);
207         assert(ss.ptr != ss2.ptr);
208 
209         auto ss3 = ss.move();
210         assert(ss.length == 0);
211         assert(ss3 == "Hello String");
212 
213         enum indented = `
214                 SELECT foo, bar, baz
215                     FROM foos
216                     WHERE id=ANY($1) AND type_id IN (
217                         SELECT id FROM bars WHERE owner=$2
218                     )`;
219 
220         enum dedented =
221                     "SELECT foo, bar, baz\n" ~
222                     "    FROM foos\n" ~
223                     "    WHERE id=ANY($1) AND type_id IN (\n" ~
224                     "        SELECT id FROM bars WHERE owner=$2\n" ~
225                     ")";
226 
227         // assert(dedent(indented) == dedented);
228 
229         printf("test run completed\n");
230         return 0;
231     }
232 }