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 }