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