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