1 /** 2 * Copyright: Copyright Jason White, 2014-2016 3 * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 * Authors: Jason White 5 */ 6 module io.text; 7 8 import io.stream; 9 import io.file.stdio : stdout; 10 import io.buffer.traits : isFlushable; 11 12 import std.functional : forward; 13 14 /** 15 * Serializes the given arguments to a text representation without a trailing 16 * new line. 17 */ 18 size_t print(Stream, T...)(Stream stream, auto ref T args) 19 if (isSink!Stream) 20 { 21 import std.conv : to; 22 23 size_t length; 24 25 foreach (arg; args) 26 length += stream.write(arg.to!string); 27 28 static if (isFlushable!Stream) 29 stream.flush(); 30 31 return length; 32 } 33 34 /// Ditto 35 size_t print(T...)(auto ref T args) 36 if (T.length > 0 && !isSink!(T[0])) 37 { 38 return stdout.print(forward!args); 39 } 40 41 unittest 42 { 43 import io.file.stream; 44 import std.typecons : tuple; 45 import std.typetuple : TypeTuple; 46 47 // First tuple value is expected output. Remaining are the types to be 48 // printed. 49 alias tests = TypeTuple!( 50 tuple(""), 51 tuple("Test", "Test"), 52 tuple("[4, 8, 15, 16, 23, 42]", [4, 8, 15, 16, 23, 42]), 53 tuple("The answer is 42", "The answer is ", 42), 54 tuple("01234567890", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0), 55 ); 56 57 auto tf = testFile(); 58 59 foreach (t; tests) 60 { 61 immutable output = t[0]; 62 63 { 64 auto f = File(tf.name, FileFlags.writeEmpty); 65 assert(f.print(t[1 .. $]) == output.length); 66 } 67 68 { 69 char[output.length] buf; 70 auto f = File(tf.name, FileFlags.readExisting); 71 assert(f.read(buf) == buf.length); 72 assert(buf == output); 73 } 74 } 75 } 76 77 /** 78 * Serializes the given arguments to a text representation followed by a new 79 * line. 80 */ 81 size_t println(T...)(auto ref T args) 82 { 83 return print(forward!args, '\n'); 84 } 85 86 unittest 87 { 88 import io.file.stream; 89 import std.typecons : tuple; 90 import std.typetuple : TypeTuple; 91 92 // First tuple value is expected output. Remaining are the types to be 93 // printed. 94 alias tests = TypeTuple!( 95 tuple("\n"), 96 tuple("Test\n", "Test"), 97 tuple("[4, 8, 15, 16, 23, 42]\n", [4, 8, 15, 16, 23, 42]), 98 tuple("The answer is 42\n", "The answer is ", 42), 99 tuple("01234567890\n", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0), 100 ); 101 102 auto tf = testFile(); 103 104 foreach (t; tests) 105 { 106 immutable output = t[0]; 107 108 { 109 auto f = File(tf.name, FileFlags.writeEmpty); 110 assert(f.println(t[1 .. $]) == output.length); 111 } 112 113 { 114 char[output.length] buf; 115 auto f = File(tf.name, FileFlags.readExisting); 116 assert(f.read(buf) == buf.length); 117 assert(buf == output); 118 } 119 } 120 } 121 122 /** 123 * Serializes the given arguments according to the given format specifier 124 * string. 125 */ 126 void printf(Stream, T...)(Stream stream, string format, auto ref T args) 127 if (isSink!Stream) 128 { 129 import std.format : formattedWrite; 130 formattedWrite(stream, forward!(format, args)); 131 } 132 133 /// Ditto 134 void printf(T...)(string format, auto ref T args) 135 { 136 stdout.printf(forward!(format, args)); 137 } 138 139 unittest 140 { 141 import io.file.stream; 142 import std.typecons : tuple; 143 import std.typetuple : TypeTuple; 144 145 // First tuple value is expected output. Remaining are the types to be 146 // printed. 147 alias tests = TypeTuple!( 148 tuple("", ""), 149 tuple("Test", "Test"), 150 tuple("The answer is 42.", "The answer is %d.", 42), 151 tuple("Hello, my name is Inigo Montoya", "Hello, my name is %s %s...", 152 "Inigo", "Montoya") 153 ); 154 155 auto tf = testFile(); 156 157 foreach (t; tests) 158 { 159 immutable output = t[0]; 160 161 { 162 auto f = File(tf.name, FileFlags.writeEmpty); 163 f.printf(t[1 .. $]); 164 } 165 166 { 167 char[output.length] buf; 168 auto f = File(tf.name, FileFlags.readExisting); 169 assert(f.read(buf) == buf.length); 170 assert(buf == output); 171 } 172 } 173 } 174 175 /** 176 * Like $(D printf), but also writes a new line. 177 */ 178 void printfln(Stream, T...)(Stream stream, string format, auto ref T args) 179 if (isSink!Stream) 180 { 181 stream.printf(forward!(format, args)); 182 stream.print('\n'); 183 } 184 185 /// Ditto 186 void printfln(T...)(string format, auto ref T args) 187 { 188 stdout.printfln(forward!(format, args)); 189 } 190 191 /** 192 * Convenience function for returning a delimiter range that iterates over 193 * lines. 194 */ 195 @property auto byLine(T = char, Stream)(Stream stream) 196 if (isSource!Stream) 197 { 198 import io.range : splitter; 199 return splitter!T(stream, '\n'); 200 } 201 202 /** 203 * Like $(D byLine), but duplicates each line. Obviously, this is less efficient 204 * than using $(D byLine). 205 */ 206 @property auto byLineCopy(T = char, Stream)(Stream stream) 207 if (isSource!Stream) 208 { 209 import io.range : splitter; 210 import std.algorithm.iteration : map; 211 return splitter!T(stream, '\n').map!(l => l.idup); 212 }