1 module toml_foolery.encode.types..string; 2 3 import std.algorithm.iteration : substitute, map; 4 import std.array : join; 5 import std.conv : to; 6 import std.format : format; 7 import std.traits : isSomeChar, isSomeString; 8 import std.uni : isControl; 9 10 import toml_foolery.encode; 11 12 13 package(toml_foolery.encode) enum bool makesTomlString(T) = ( 14 isSomeChar!T || isSomeString!T 15 ); 16 17 /// Serializes (w/d/)strings and (w/d/)chars into TOML string values, quoted and escaped. 18 package(toml_foolery.encode) void tomlifyValueImpl(T)( 19 const T value, 20 ref Appender!string buffer, 21 immutable string[] parentTables 22 ) 23 if (makesTomlString!T) 24 { 25 buffer.put(`"`); 26 buffer.put(value.to!string.escaped); 27 buffer.put(`"`); 28 } 29 30 private string escaped(string s) 31 { 32 return s.substitute!( 33 "\"", `\"`, 34 "\\", `\\`, 35 "\b", `\b`, 36 "\f", `\f`, 37 "\n", `\n`, 38 "\r", `\r`, 39 ).map!((e) 40 { 41 if (isControl(e) && e != dchar('\t')) 42 { 43 return `\u%04X`.format(cast(long)e); 44 } 45 else 46 { 47 return e.to!string; 48 } 49 }).join; 50 } 51 52 @("Encode `string` values") 53 unittest 54 { 55 string str = "Eskarina"; 56 expect(_tomlifyValue(str)).toEqual(`"Eskarina"`); 57 } 58 59 @("Encode `wstring` values") 60 unittest 61 { 62 wstring wstr = "Weskarina"; 63 expect(_tomlifyValue(wstr)).toEqual(`"Weskarina"`); 64 } 65 66 @("Encode `dstring` values") 67 unittest 68 { 69 dstring dstr = "Deskarina"; 70 expect(_tomlifyValue(dstr)).toEqual(`"Deskarina"`); 71 } 72 73 @("Encode strings with multi-codepoint unicode characters") 74 unittest 75 { 76 string a = "🍕👨👩👧🜀"; 77 expect(_tomlifyValue(a)).toEqual(`"🍕👨👩👧🜀"`); 78 79 wstring b = "👨👩👧🜀🍕"w; 80 expect(_tomlifyValue(b)).toEqual(`"👨👩👧🜀🍕"`); 81 82 dstring c = "🜀🍕👨👩👧"d; 83 expect(_tomlifyValue(c)).toEqual(`"🜀🍕👨👩👧"`); 84 } 85 86 @("Encode `char` values as Strings") 87 unittest 88 { 89 char c = '*'; 90 expect(_tomlifyValue(c)).toEqual(`"*"`); 91 } 92 93 @("Encode `wchar` values as Strings") 94 unittest 95 { 96 wchar w = 'ⵖ'; 97 expect(_tomlifyValue(w)).toEqual(`"ⵖ"`); 98 } 99 100 @("Encode `dchar` values as Strings") 101 unittest 102 { 103 dchar d = '🌻'; 104 expect(_tomlifyValue(d)).toEqual(`"🌻"`); 105 } 106 107 @("Escape characters that need to be escaped") 108 unittest 109 { 110 /++ 111 + From the TOML readme: 112 + 113 + 114 + Any Unicode character may be used except those that must be escaped: 115 + quotation mark, backslash, and the control characters other than tab 116 + (U+0000 to U+0008, U+000A to U+001F, U+007F). 117 + 118 + For convenience, some popular characters have a compact escape sequence. 119 + \b - backspace (U+0008) 120 + \t - tab (U+0009) 121 + \n - linefeed (U+000A) 122 + \f - form feed (U+000C) 123 + \r - carriage return (U+000D) 124 + \" - quote (U+0022) 125 + \\ - backslash (U+005C) 126 + \uXXXX - unicode (U+XXXX) 127 + \UXXXXXXXX - unicode (U+XXXXXXXX) 128 +/ 129 130 string compactSequences = "\"\\\b\f\n\r"; 131 expect(_tomlifyValue(compactSequences)).toEqual(`"\"\\\b\f\n\r"`); 132 133 string nonCompactSequences = "\u0001\U0000007f\x00"; 134 expect(_tomlifyValue(nonCompactSequences)).toEqual(`"\u0001\u007F\u0000"`); 135 136 string dontEscapeTab = "\t"; 137 expect(_tomlifyValue(dontEscapeTab)).toEqual("\"\t\""); 138 }