| 1 | import strings |
| 2 | |
| 3 | type MyInt = int |
| 4 | |
| 5 | const maxn = 100000 |
| 6 | |
| 7 | fn test_sb() { |
| 8 | mut sb := strings.new_builder(100) |
| 9 | sb.write_string('hi') |
| 10 | sb.write_string('!') |
| 11 | sb.write_string('hello') |
| 12 | assert sb.len == 8 |
| 13 | sb_end := sb.str() |
| 14 | assert sb_end == 'hi!hello' |
| 15 | assert sb.len == 0 |
| 16 | /// |
| 17 | sb = strings.new_builder(10) |
| 18 | sb.write_string('a') |
| 19 | sb.write_string('b') |
| 20 | assert sb.len == 2 |
| 21 | assert sb.str() == 'ab' |
| 22 | // Test interpolation optimization |
| 23 | sb = strings.new_builder(10) |
| 24 | x := 10 |
| 25 | y := MyInt(20) |
| 26 | sb.writeln('x = ${x} y = ${y}') |
| 27 | res := sb.str() |
| 28 | assert res[res.len - 1] == `\n` |
| 29 | println('"${res}"') |
| 30 | assert res.trim_space() == 'x = 10 y = 20' |
| 31 | |
| 32 | sb = strings.new_builder(10) |
| 33 | sb.write_string('x = ${x} y = ${y}') |
| 34 | assert sb.str() == 'x = 10 y = 20' |
| 35 | //$if !windows { |
| 36 | sb = strings.new_builder(10) |
| 37 | sb.write_string('123456') |
| 38 | last_2 := sb.cut_last(2) |
| 39 | assert last_2 == '56' |
| 40 | final_sb := sb.str() |
| 41 | assert final_sb == '1234' |
| 42 | //} |
| 43 | sb.clear() |
| 44 | assert sb.str() == '' |
| 45 | } |
| 46 | |
| 47 | fn test_big_sb() { |
| 48 | mut sb := strings.new_builder(100) |
| 49 | mut sb2 := strings.new_builder(10000) |
| 50 | for i in 0 .. maxn { |
| 51 | sb.writeln(i.str()) |
| 52 | sb2.write_string('+') |
| 53 | } |
| 54 | s := sb.str() |
| 55 | lines := s.split_into_lines() |
| 56 | assert lines.len == maxn |
| 57 | assert lines[0] == '0' |
| 58 | assert lines[1] == '1' |
| 59 | assert lines[777] == '777' |
| 60 | assert lines[98765] == '98765' |
| 61 | println(sb2.len) |
| 62 | assert sb2.len == maxn |
| 63 | } |
| 64 | |
| 65 | fn test_byte_write() { |
| 66 | mut sb := strings.new_builder(100) |
| 67 | temp_str := 'byte testing' |
| 68 | mut count := 0 |
| 69 | for word in temp_str { |
| 70 | sb.write_u8(word) |
| 71 | count++ |
| 72 | assert count == sb.len |
| 73 | } |
| 74 | sb_final := sb.str() |
| 75 | assert sb_final == temp_str |
| 76 | } |
| 77 | |
| 78 | fn test_strings_builder_reuse() { |
| 79 | mut sb := strings.new_builder(256) |
| 80 | sb.write_string('world') |
| 81 | assert sb.str() == 'world' |
| 82 | sb.write_string('hello') |
| 83 | assert sb.str() == 'hello' |
| 84 | } |
| 85 | |
| 86 | fn test_cut_to() { |
| 87 | mut sb := strings.new_builder(16) |
| 88 | sb.write_string('hello') |
| 89 | assert sb.cut_to(3) == 'lo' |
| 90 | assert sb.len == 3 |
| 91 | assert sb.cut_to(3) == '' |
| 92 | assert sb.len == 3 |
| 93 | assert sb.cut_to(0) == 'hel' |
| 94 | assert sb.cut_to(32) == '' |
| 95 | assert sb.len == 0 |
| 96 | } |
| 97 | |
| 98 | fn test_write_rune() { |
| 99 | mut sb := strings.new_builder(10) |
| 100 | sb.write_rune(`h`) |
| 101 | sb.write_rune(`e`) |
| 102 | sb.write_rune(`l`) |
| 103 | sb.write_rune(`l`) |
| 104 | sb.write_rune(`o`) |
| 105 | x := sb.str() |
| 106 | assert x == 'hello' |
| 107 | } |
| 108 | |
| 109 | fn test_write_runes() { |
| 110 | mut sb := strings.new_builder(20) |
| 111 | sb.write_runes([`h`, `e`, `l`, `l`, `o`]) |
| 112 | sb.write_rune(` `) |
| 113 | sb.write_runes([`w`, `o`, `r`, `l`, `d`]) |
| 114 | x := sb.str() |
| 115 | assert x == 'hello world' |
| 116 | } |
| 117 | |
| 118 | fn test_ensure_cap() { |
| 119 | mut sb := strings.new_builder(0) |
| 120 | assert sb.cap == 0 |
| 121 | sb.ensure_cap(10) |
| 122 | assert sb.cap >= 10 |
| 123 | old_cap := sb.cap |
| 124 | sb.ensure_cap(10) |
| 125 | assert sb.cap == old_cap |
| 126 | sb.ensure_cap(15) |
| 127 | assert sb.cap >= 15 |
| 128 | old_cap2 := sb.cap |
| 129 | sb.ensure_cap(10) |
| 130 | assert sb.cap == old_cap2 |
| 131 | sb.ensure_cap(-1) |
| 132 | assert sb.cap == old_cap2 |
| 133 | } |
| 134 | |
| 135 | fn test_drain_builder() { |
| 136 | mut sb := strings.new_builder(0) |
| 137 | mut target_sb := strings.new_builder(0) |
| 138 | assert sb.cap == 0 |
| 139 | assert target_sb.cap == 0 |
| 140 | |
| 141 | sb.write_string('abc') |
| 142 | assert sb.len == 3 |
| 143 | |
| 144 | target_sb.drain_builder(mut sb, 0) |
| 145 | assert sb.len == 0 |
| 146 | assert target_sb.len == 3 |
| 147 | assert target_sb.str() == 'abc' |
| 148 | } |
| 149 | |
| 150 | @[manualfree] |
| 151 | fn sb_i64_str(n i64) string { |
| 152 | mut sb := strings.new_builder(24) |
| 153 | defer { |
| 154 | unsafe { sb.free() } |
| 155 | } |
| 156 | sb.write_decimal(n) |
| 157 | return sb.str() |
| 158 | } |
| 159 | |
| 160 | fn test_write_decimal() { |
| 161 | assert sb_i64_str(0) == '0' |
| 162 | assert sb_i64_str(1) == '1' |
| 163 | assert sb_i64_str(-1) == '-1' |
| 164 | assert sb_i64_str(1001) == '1001' |
| 165 | assert sb_i64_str(-1001) == '-1001' |
| 166 | assert sb_i64_str(1234567890) == '1234567890' |
| 167 | assert sb_i64_str(-1234567890) == '-1234567890' |
| 168 | assert sb_i64_str(9223372036854775807) == '9223372036854775807' |
| 169 | assert sb_i64_str(-9223372036854775807) == '-9223372036854775807' |
| 170 | assert sb_i64_str(min_i64) == '-9223372036854775808' |
| 171 | } |
| 172 | |
| 173 | fn test_grow_len() { |
| 174 | mut sb := strings.new_builder(10) |
| 175 | assert sb.len == 0 |
| 176 | assert sb.cap == 10 |
| 177 | |
| 178 | sb.write_string('0123456789') |
| 179 | assert sb.len == 10 |
| 180 | |
| 181 | unsafe { sb.grow_len(-5) } |
| 182 | assert sb.len == 10 |
| 183 | assert sb.cap == 10 |
| 184 | |
| 185 | unsafe { sb.grow_len(10) } |
| 186 | assert sb.len == 20 |
| 187 | assert sb.cap == 20 |
| 188 | |
| 189 | sb.ensure_cap(35) |
| 190 | assert sb.len == 20 |
| 191 | assert sb.cap >= 35 |
| 192 | cap_after_ensure := sb.cap |
| 193 | |
| 194 | unsafe { sb.grow_len(5) } |
| 195 | assert sb.len == 25 |
| 196 | assert sb.cap == cap_after_ensure |
| 197 | } |
| 198 | |
| 199 | fn test_write_repeated_rune() { |
| 200 | mut sb := strings.new_builder(20) |
| 201 | sb.write_repeated_rune(`h`, 5) |
| 202 | sb.write_repeated_rune(`w`, 5) |
| 203 | sb.write_repeated_rune(`√`, 5) |
| 204 | sb.write_rune(` `) |
| 205 | x := sb.str() |
| 206 | assert x == 'hhhhhwwwww√√√√√ ' |
| 207 | } |
| 208 | |
| 209 | struct IndentTest { |
| 210 | param strings.IndentParam |
| 211 | input string |
| 212 | output string |
| 213 | } |
| 214 | |
| 215 | // vfmt off |
| 216 | const indent_test_data = [ |
| 217 | IndentTest{ |
| 218 | input: 'User1 { |
| 219 | name: "John" |
| 220 | settings: { |
| 221 | theme: "dark" |
| 222 | language: "en" |
| 223 | } |
| 224 | }' |
| 225 | output: 'User1 { |
| 226 | name: "John" |
| 227 | settings: { |
| 228 | theme: "dark" |
| 229 | language: "en" |
| 230 | } |
| 231 | }' |
| 232 | }, |
| 233 | IndentTest{ |
| 234 | input: 'User2{name:"John" settings:{theme:"dark" language:"en" hobbies:["reading","sports"]}}' |
| 235 | output: 'User2{ |
| 236 | name:"John" settings:{ |
| 237 | theme:"dark" language:"en" hobbies:["reading","sports"] |
| 238 | } |
| 239 | }' |
| 240 | }, |
| 241 | IndentTest{ |
| 242 | input: 'message {text: "Hello {world}!" count: 5 nested: {data: "Test {inner}"}}' |
| 243 | output: 'message { |
| 244 | text: "Hello {world}!" count: 5 nested: { |
| 245 | data: "Test {inner}" |
| 246 | } |
| 247 | }' |
| 248 | }, |
| 249 | IndentTest{ |
| 250 | input: 'if x > 0 {println("Positive") for i in 0..x {println(i) if i % 2 == 0 {println("even")}}} else {println("Not positive")}' |
| 251 | output: 'if x > 0 { |
| 252 | println("Positive") for i in 0..x { |
| 253 | println(i) if i % 2 == 0 { |
| 254 | println("even") |
| 255 | } |
| 256 | } |
| 257 | } else { |
| 258 | println("Not positive") |
| 259 | }' |
| 260 | }, |
| 261 | IndentTest{ |
| 262 | input: 'config{database:{host:"localhost" port:5432} server:{port:8080 routes:{api:"/api" static:"/static"}} log:{level:"info"}}' |
| 263 | output: 'config{ |
| 264 | database:{ |
| 265 | host:"localhost" port:5432 |
| 266 | } server:{ |
| 267 | port:8080 routes:{ |
| 268 | api:"/api" static:"/static" |
| 269 | } |
| 270 | } log:{ |
| 271 | level:"info" |
| 272 | } |
| 273 | }' |
| 274 | }, |
| 275 | IndentTest{ |
| 276 | input: "MyS{ |
| 277 | a: 0 |
| 278 | b: 0 |
| 279 | son: Son{ |
| 280 | k: '' |
| 281 | m: '' |
| 282 | } |
| 283 | } |
| 284 | " |
| 285 | output: "MyS{ |
| 286 | a: 0 |
| 287 | b: 0 |
| 288 | son: Son{ |
| 289 | k: '' |
| 290 | m: '' |
| 291 | } |
| 292 | } |
| 293 | " |
| 294 | }, |
| 295 | IndentTest{ |
| 296 | input: 'config {message: "He said: \\"Hello World!\\"" code: "if (x) { return \\"value\\"; }"}' |
| 297 | output: 'config { |
| 298 | message: "He said: \\"Hello World!\\"" code: "if (x) { return \\"value\\"; }" |
| 299 | }' |
| 300 | }, |
| 301 | IndentTest { |
| 302 | input : 'data {text: "String with {curly braces} and \\"quotes\\"" nested: {value: "Inner \\"quoted\\" string"}}' |
| 303 | output : 'data { |
| 304 | text: "String with {curly braces} and \\"quotes\\"" nested: { |
| 305 | value: "Inner \\"quoted\\" string" |
| 306 | } |
| 307 | }' |
| 308 | }, |
| 309 | IndentTest { |
| 310 | input : 'escape {backslash: "Path: C:\\\\Users\\\\Test" newline: "Line1\\nLine2" tab: "Column1\\tColumn2"}' |
| 311 | output : 'escape { |
| 312 | backslash: "Path: C:\\\\Users\\\\Test" newline: "Line1\\nLine2" tab: "Column1\\tColumn2" |
| 313 | }' |
| 314 | }, |
| 315 | IndentTest { |
| 316 | input : 'nested {outer: "Outer \\"quote\\" with {braces}" inner: {value: "Inner string with \\\\backslash and \\"quotes\\""}}' |
| 317 | output : 'nested { |
| 318 | outer: "Outer \\"quote\\" with {braces}" inner: { |
| 319 | value: "Inner string with \\\\backslash and \\"quotes\\"" |
| 320 | } |
| 321 | }' |
| 322 | }, |
| 323 | IndentTest { |
| 324 | input : 'complex {str1: "\\"Escaped quotes\\"" str2: "Backslash: \\\\" str3: "Mixed: \\"quoted\\" and \\\\backslash" regex: "Pattern: \\\\d+\\\\.\\\\d+"}' |
| 325 | output : 'complex { |
| 326 | str1: "\\"Escaped quotes\\"" str2: "Backslash: \\\\" str3: "Mixed: \\"quoted\\" and \\\\backslash" regex: "Pattern: \\\\d+\\\\.\\\\d+" |
| 327 | }' |
| 328 | }, |
| 329 | IndentTest { |
| 330 | input : 'json {data: "{\\"name\\": \\"John\\", \\"age\\": 30, \\"city\\": \\"New York\\"}"}' |
| 331 | output : 'json { |
| 332 | data: "{\\"name\\": \\"John\\", \\"age\\": 30, \\"city\\": \\"New York\\"}" |
| 333 | }' |
| 334 | }, |
| 335 | IndentTest { |
| 336 | input : 'code {function: "fn test() { if (x) { return \\\"value\\\"; } }" error: "Error: \\\"File not found\\\" at line 10"}' |
| 337 | output : 'code { |
| 338 | function: "fn test() { if (x) { return \\\"value\\\"; } }" error: "Error: \\\"File not found\\\" at line 10" |
| 339 | }' |
| 340 | }, |
| 341 | IndentTest { |
| 342 | input : 'multiline {message: "Line 1\\nLine 2\\nLine 3 with {braces}\\nLine 4 with \\\"quotes\\\""}' |
| 343 | output : 'multiline { |
| 344 | message: "Line 1\\nLine 2\\nLine 3 with {braces}\\nLine 4 with \\\"quotes\\\"" |
| 345 | }' |
| 346 | }, |
| 347 | IndentTest { |
| 348 | input : 'extreme {str: "\\\\\\\\\\\\\\"Quadruple escaped\\\\\\\\\\\\\\""}' |
| 349 | output : 'extreme { |
| 350 | str: "\\\\\\\\\\\\\\"Quadruple escaped\\\\\\\\\\\\\\"" |
| 351 | }' |
| 352 | }, |
| 353 | IndentTest{ |
| 354 | param: strings.IndentParam { |
| 355 | block_start: `[` |
| 356 | block_end: `]` |
| 357 | } |
| 358 | input: 'message [text: "Hello {world}!" count: 5 nested: [data: "Test {inner}"]]' |
| 359 | output: 'message [ |
| 360 | text: "Hello {world}!" count: 5 nested: [ |
| 361 | data: "Test {inner}" |
| 362 | ] |
| 363 | ]' |
| 364 | }, |
| 365 | IndentTest{ |
| 366 | param: strings.IndentParam { |
| 367 | block_start: `[` |
| 368 | block_end: `]` |
| 369 | indent_char: `#` |
| 370 | } |
| 371 | input: 'message [text: "Hello {world}!" count: 5 nested: [data: "Test {inner}"]]' |
| 372 | output: 'message [ |
| 373 | ####text: "Hello {world}!" count: 5 nested: [ |
| 374 | ########data: "Test {inner}" |
| 375 | ####] |
| 376 | ]' |
| 377 | }, |
| 378 | IndentTest{ |
| 379 | param: strings.IndentParam { |
| 380 | block_start: `[` |
| 381 | block_end: `]` |
| 382 | indent_char: `\t` |
| 383 | indent_count: 2 |
| 384 | } |
| 385 | input: 'message [text: "Hello {world}!" count: 5 nested: [data: "Test {inner}"]]' |
| 386 | output: 'message [ |
| 387 | \t\ttext: "Hello {world}!" count: 5 nested: [ |
| 388 | \t\t\t\tdata: "Test {inner}" |
| 389 | \t\t] |
| 390 | ]' |
| 391 | }, |
| 392 | IndentTest{ |
| 393 | param: strings.IndentParam { |
| 394 | block_start: `[` |
| 395 | block_end: `]` |
| 396 | indent_char: `#` |
| 397 | indent_count: 1 |
| 398 | starting_level: 2 |
| 399 | } |
| 400 | input: 'message [text: "Hello {world}!" count: 5 nested: [data: "Test {inner}"]]' |
| 401 | output: '##message [ |
| 402 | ###text: "Hello {world}!" count: 5 nested: [ |
| 403 | ####data: "Test {inner}" |
| 404 | ###] |
| 405 | ##]' |
| 406 | }, |
| 407 | ] |
| 408 | |
| 409 | // vfmt on |
| 410 | |
| 411 | fn test_indent() { |
| 412 | for t in indent_test_data { |
| 413 | mut sb := strings.new_builder(256) |
| 414 | sb.indent(t.input, t.param) |
| 415 | assert sb.str() == t.output |
| 416 | } |
| 417 | } |
| 418 | |