1 /** 2 * Structured INI 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 deprecated module ae.utils.serialization.sini; 15 deprecated: 16 17 import std.exception; 18 import std.range; 19 import std.string; 20 import std.traits; 21 22 import ae.utils.meta.binding; 23 import ae.utils.meta.reference; 24 25 struct IniParser(R) 26 { 27 static void setValue(S, Sink)(S[] segments, S value, Sink sink) 28 { 29 if (segments.length == 0) 30 sink.handleString(value); 31 else 32 { 33 struct Reader 34 { 35 // https://issues.dlang.org/show_bug.cgi?id=12318 36 void dummy() {} 37 38 void read(Sink)(Sink sink) 39 { 40 setValue(segments[1..$], value, sink); 41 } 42 } 43 Reader reader; 44 sink.traverse(segments[0], boundFunctorOf!(Reader.read)(&reader)); 45 } 46 } 47 48 static S readSection(R, S, Sink)(ref R r, S[] segments, Sink sink) 49 { 50 if (segments.length) 51 { 52 struct Reader 53 { 54 // https://issues.dlang.org/show_bug.cgi?id=12318 55 void dummy() {} 56 57 S read(Sink)(Sink sink) 58 { 59 return readSection(r, segments[1..$], sink); 60 } 61 } 62 Reader reader; 63 return sink.traverse(segments[0], boundFunctorOf!(Reader.read)(&reader)); 64 } 65 66 while (!r.empty) 67 { 68 auto line = r.front.chomp().stripLeft(); 69 70 scope(success) r.popFront(); 71 if (line.empty) 72 continue; 73 if (line[0] == '#' || line[0] == ';') 74 continue; 75 76 if (line.startsWith('[')) 77 { 78 line = line.stripRight(); 79 enforce(line[$-1] == ']', "Malformed section line (no ']')"); 80 return line[1..$-1]; 81 } 82 83 auto pos = line.indexOf('='); 84 enforce(pos > 0, "Malformed value line (no '=')"); 85 auto name = line[0..pos].strip; 86 segments = name.split("."); 87 enforce(segments.length, "Malformed value line (empty name)"); 88 setValue(segments, line[pos+1..$].strip, sink); 89 } 90 return null; 91 } 92 93 void parseIni(R, Sink)(R r, Sink sink) 94 { 95 auto nextSection = readSection(r, typeof(r.front)[].init, sink); 96 97 while (nextSection) 98 nextSection = readSection(r, nextSection.split("."), sink); 99 } 100 } 101 102 /// Parse a structured INI from a range of lines, into a user-defined struct. 103 T parseIni(T, R)(R r) 104 if (isInputRange!R && isSomeString!(ElementType!R)) 105 { 106 import ae.utils.serialization.serialization; 107 108 T result; 109 auto parser = IniParser!R(); 110 parser.parseIni(r, deserializer(&result)); 111 return result; 112 } 113 114 unittest 115 { 116 static struct File 117 { 118 struct S 119 { 120 string n1, n2; 121 int[string] a; 122 } 123 S s; 124 } 125 126 auto f = parseIni!File 127 ( 128 q"< 129 s.n1=v1 130 s.a.foo=1 131 [s] 132 n2=v2 133 a.bar=2 134 >".splitLines() 135 ); 136 137 assert(f.s.n1=="v1"); 138 assert(f.s.n2=="v2"); 139 assert(f.s.a==["foo":1, "bar":2]); 140 } 141 142 unittest 143 { 144 import ae.utils.serialization.serialization; 145 import std.conv; 146 147 static struct Custom 148 { 149 struct Section 150 { 151 string name; 152 string[string] values; 153 } 154 Section[] sections; 155 156 enum isSerializationSink = true; 157 158 auto traverse(Reader)(wstring name, Reader reader) 159 { 160 sections.length++; 161 auto p = §ions[$-1]; 162 p.name = to!string(name); 163 return reader(deserializer(&p.values)); 164 } 165 166 void handleString(S)(S s) { assert(false); } 167 } 168 169 auto c = parseIni!Custom 170 ( 171 q"< 172 [one] 173 a=a 174 [two] 175 b=b 176 >"w.splitLines() 177 ); 178 179 assert(c == Custom([Custom.Section("one", ["a" : "a"]), Custom.Section("two", ["b" : "b"])])); 180 } 181 182 // *************************************************************************** 183 184 /// Convenience function to load a struct from an INI file. 185 /// Returns .init if the file does not exist. 186 S loadIni(S)(string fileName) 187 { 188 S s; 189 190 import std.file; 191 if (fileName.exists) 192 s = fileName 193 .readText() 194 .splitLines() 195 .parseIni!S(); 196 197 return s; 198 }