ops.ha (4298B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use encoding::utf8; 5 use io; 6 use strings; 7 8 // Appends zero or more strings to an [[io::handle]]. The output needn't be a 9 // memio stream, but it's generally more efficient if it is. Returns the number 10 // of bytes written, or an error. 11 export fn concat(out: io::handle, strs: str...) (size | io::error) = 12 join(out, "", strs...); 13 14 @test fn concat() void = { 15 let st = dynamic(); 16 defer io::close(&st)!; 17 let tests: [_]([]str, str) = [ 18 ([], ""), 19 ([""], ""), 20 (["", ""], ""), 21 (["hello"], "hello"), 22 (["hello", " ", "world"], "hello world"), 23 (["", "hello", " ", "world"], "hello world"), 24 (["hello", " ", "world", ""], "hello world"), 25 (["hello", "", " ", "world"], "hello world") 26 ]; 27 for (let i = 0z; i < len(tests); i += 1) { 28 let ln = concat(&st, tests[i].0...) as size; 29 assert(ln == len(tests[i].1) && string(&st)! == tests[i].1); 30 reset(&st); 31 }; 32 }; 33 34 // Joins several strings together by a delimiter and writes them to a handle. 35 // The output needn't be a memio stream, but it's generally more efficient if it 36 // is. Returns the number of bytes written, or an error. 37 export fn join(out: io::handle, delim: str, strs: str...) (size | io::error) = { 38 let n = 0z; 39 let delim = strings::toutf8(delim); 40 for (let i = 0z; i < len(strs); i += 1) { 41 n += io::writeall(out, strings::toutf8(strs[i]))?; 42 if (len(delim) != 0 && i + 1 < len(strs)) { 43 n += io::writeall(out, delim)?; 44 }; 45 }; 46 return n; 47 }; 48 49 @test fn join() void = { 50 let st = dynamic(); 51 defer io::close(&st)!; 52 let tests: [_](str, []str, str) = [ 53 ("::", [], ""), 54 ("::", [""], ""), 55 ("::", ["", ""], "::"), 56 ("::", ["", "", ""], "::::"), 57 ("::", ["hello"], "hello"), 58 ("::", ["hello", "world"], "hello::world"), 59 ("::", ["", "hello", "world"], "::hello::world"), 60 ("::", ["hello", "world", ""], "hello::world::"), 61 ("::", ["hello", "", "world"], "hello::::world"), 62 ]; 63 for (let i = 0z; i < len(tests); i += 1) { 64 let ln = join(&st, tests[i].0, tests[i].1...) as size; 65 assert(ln == len(tests[i].2) && string(&st)! == tests[i].2); 66 reset(&st); 67 }; 68 }; 69 70 // Appends zero or more strings to an [[io::handle]], in reverse order. The 71 // output needn't be a memio stream, but it's generally more efficient if it is. 72 // Returns the number of bytes written, or an error. 73 export fn rconcat(out: io::handle, strs: str...) (size | io::error) = 74 rjoin(out, "", strs...); 75 76 @test fn rconcat() void = { 77 let st = dynamic(); 78 defer io::close(&st)!; 79 let tests: [_]([]str, str) = [ 80 ([], ""), 81 ([""], ""), 82 (["", ""], ""), 83 (["hello"], "hello"), 84 (["hello", " ", "world"], "world hello"), 85 (["", "hello", " ", "world"], "world hello"), 86 (["hello", " ", "world", ""], "world hello"), 87 (["hello", "", " ", "world"], "world hello") 88 ]; 89 for (let i = 0z; i < len(tests); i += 1) { 90 let ln = rconcat(&st, tests[i].0...) as size; 91 assert(ln == len(tests[i].1) && string(&st)! == tests[i].1); 92 reset(&st); 93 }; 94 }; 95 96 // Joins several strings together by a delimiter and writes them to a handle, in 97 // reverse order. The output needn't be a memio stream, but it's generally more 98 // efficient if it is. Returns the number of bytes written, or an error. 99 export fn rjoin(out: io::handle, delim: str, strs: str...) (size | io::error) = { 100 let n = 0z; 101 let delim = strings::toutf8(delim); 102 for (let i = len(strs); i > 0; i -= 1) { 103 n += io::writeall(out, strings::toutf8(strs[i - 1]))?; 104 if (len(delim) != 0 && i - 1 > 0) { 105 n += io::writeall(out, delim)?; 106 }; 107 }; 108 return n; 109 }; 110 111 @test fn rjoin() void = { 112 let st = dynamic(); 113 defer io::close(&st)!; 114 let tests: [_](str, []str, str) = [ 115 ("::", [], ""), 116 ("::", [""], ""), 117 ("::", ["", ""], "::"), 118 ("::", ["", "", ""], "::::"), 119 ("::", ["hello"], "hello"), 120 ("::", ["hello", "world"], "world::hello"), 121 ("::", ["", "hello", "world"], "world::hello::"), 122 ("::", ["hello", "world", ""], "::world::hello"), 123 ("::", ["hello", "", "world"], "world::::hello"), 124 ]; 125 for (let i = 0z; i < len(tests); i += 1) { 126 let ln = rjoin(&st, tests[i].0, tests[i].1...) as size; 127 assert(ln == len(tests[i].2) && string(&st)! == tests[i].2); 128 reset(&st); 129 }; 130 }; 131 132 // Appends a rune to a stream. 133 export fn appendrune(out: io::handle, r: rune) (size | io::error) = 134 io::writeall(out, utf8::encoderune(r));