On Tuesday, 23 August 2022 at 23:17:21 UTC, Salih Dincer wrote:
The nice thing here is that you can change the format specifiers as you wish.
Actually, both structures could be combined: ```d struct EscapedString { string[1] str; this(string str) @nogc pure nothrow @safe { this.str[0] = str; } import std.format, std.range; void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)const { if(fmt.spec == 'e') { char buf; formattedWrite((const(char)[] data) { if(!data.length) return; if(buf < 0xff) put(sink, buf); else data = data[1 .. $]; if(data.length) { put(sink, data[0 .. $-1]); buf = data[$-1]; } else { buf = 0; return; } }, "%(%s%)", str[]); } else sink.formattedWrite("%s", str[0]); } } unittest { import std.array : appender; import std.format; auto str = EscapedString(); auto app = appender!string; app.formattedWrite("%e", str); assert(app.data.length == 0); auto results = [`a\tb`, `a\tba\nb`, `a\tba\nba\rb`, `a\tba\nba\rba b`, `a\tba\nba\rba ba\"b` ]; foreach(i, char c; [9, 10, 13, 32, 34]) { const s = "a" ~ c ~ "b"; app.formattedWrite("%e", EscapedString(s)); assert(app.data == results[i], format("%s, i = %s", app.data, i)); } } ``` SDB@79