• R/O
  • HTTP
  • SSH
  • HTTPS

提交

標籤

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

A D package containing my single-file modules


Commit MetaInfo

修訂a74177c5e751f0c002664d261acf4574ed72bc36 (tree)
時間2023-11-02 10:08:25
作者nemophila <stigma+osdn@disr...>
Commiternemophila

Log Message

configparser: reformat and add attributes

Change Summary

差異

--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,6 @@
1+[*.d]
2+indent_style = space
3+indent_size = 3
4+end_of_line = lf
5+trim_trailing_whitespace = true
6+insert_final_newline = true
--- a/source/mlib/configparser.d
+++ b/source/mlib/configparser.d
@@ -52,19 +52,19 @@ import std.range.primitives : isOutputRange;
5252
5353 public class DuplicateSectionException : Exception
5454 {
55- private string m_section;
55+ private string m_section;
5656
57- this(string section) @safe pure
58- {
59- string msg = "Section " ~ section ~ " already exists.";
60- m_section = section;
61- super(msg);
62- }
57+ this(string section) @safe pure
58+ {
59+ const msg = "Section " ~ section ~ " already exists.";
60+ m_section = section;
61+ super(msg);
62+ }
6363
64- string section()
65- {
66- return m_section;
67- }
64+ string section() const @safe pure
65+ {
66+ return m_section;
67+ }
6868 }
6969
7070 ///
@@ -73,62 +73,67 @@ public class DuplicateSectionException : Exception
7373 ///
7474 public class DuplicateOptionException : Exception
7575 {
76- private string m_option;
77- private string m_section;
76+ private string m_option;
77+ private string m_section;
7878
79- this(string option, string section) @safe pure
80- {
81- string msg = "Option " ~ option ~ " in section " ~ section ~
82- " already exists.";
83- m_option = option;
84- m_section = section;
85- super(msg);
86- }
79+ this(string option, string section) @safe pure
80+ {
81+ const msg = "Option " ~ option ~ " in section " ~ section ~ " already exists.";
82+ m_option = option;
83+ m_section = section;
84+ super(msg);
85+ }
8786
88- string option()
89- {
90- return m_option;
91- }
87+ string option() const @safe pure
88+ {
89+ return m_option;
90+ }
9291
93- string section()
94- {
95- return m_section;
96- }
92+ string section() const @safe pure
93+ {
94+ return m_section;
95+ }
9796 }
9897
9998 public class NoSectionException : Exception
10099 {
101- private string m_section;
100+ private string m_section;
102101
103- this(string section) @safe pure
104- {
105- string msg = "Section '" ~ section ~ "' does not exist.";
106- m_section = section;
107- super(msg);
108- }
102+ this(string section) @safe pure
103+ {
104+ const msg = "Section '" ~ section ~ "' does not exist.";
105+ m_section = section;
106+ super(msg);
107+ }
109108
110- string section()
111- {
112- return m_section;
113- }
109+ string section() const @safe pure
110+ {
111+ return m_section;
112+ }
114113 }
115114
116115 public class NoOptionException : Exception
117116 {
118- private string m_section;
119- private string m_option;
117+ private string m_section;
118+ private string m_option;
120119
121- this(string section, string option) @safe pure
122- {
123- string msg = "Section '" ~ section ~ "' does not have option '" ~
124- option ~ "'.";
125- m_section = section;
126- m_option = option;
127- super(msg);
128- }
120+ this(string section, string option) @safe pure
121+ {
122+ const msg = "Section '" ~ section ~ "' does not have option '" ~ option ~ "'.";
123+ m_section = section;
124+ m_option = option;
125+ super(msg);
126+ }
127+
128+ string section() const @safe pure
129+ {
130+ return m_section;
131+ }
129132
130- string section() { return m_section; }
131- string option() { return m_option; }
133+ string option() const @safe pure
134+ {
135+ return m_option;
136+ }
132137 }
133138
134139 /**
@@ -155,308 +160,336 @@ public class ConfigParser
155160 m_strict = strict;
156161 }
157162
158- /**
159- * Return an array containing the available sections.
160- */
161- string[] sections() @safe pure
162- {
163- return m_sections.keys();
164- }
163+ /**
164+ * Return an array containing the available sections.
165+ */
166+ string[] sections() const @safe pure
167+ {
168+ return m_sections.keys();
169+ }
165170
166- ///
167- @safe pure unittest
168- {
169- auto conf = new ConfigParser();
171+ ///
172+ @safe pure unittest
173+ {
174+ auto conf = new ConfigParser();
170175
171- assert(0 == conf.sections().length);
176+ assert(0 == conf.sections().length);
172177
173- conf.addSection("Section");
178+ conf.addSection("Section");
174179
175- assert(1 == conf.sections().length);
176- }
180+ assert(1 == conf.sections().length);
181+ }
177182
178- /**
179- * Add a section named `section` to the instance.
180- *
181- * Throws:
182- * - DuplicateSectionError if a section by the given name already
183- * exists.
184- */
185- void addSection(string section) @safe pure
186- {
187- if (section in m_sections)
188- throw new DuplicateSectionException(section);
189- m_sections[section] = null;
190- }
183+ /**
184+ * Add a section named `section` to the instance.
185+ *
186+ * Throws:
187+ * - DuplicateSectionError if a section by the given name already
188+ * exists.
189+ */
190+ void addSection(string section) @safe pure
191+ {
192+ if (section in m_sections)
193+ {
194+ throw new DuplicateSectionException(section);
195+ }
196+ m_sections[section] = null;
197+ }
191198
192- ///
193- unittest
194- {
195- import std.exception : assertNotThrown, assertThrown;
199+ ///
200+ unittest
201+ {
202+ import std.exception : assertNotThrown, assertThrown;
196203
197- auto conf = new ConfigParser();
204+ auto conf = new ConfigParser();
198205
199- /* doesn't yet exist */
200- assertNotThrown!DuplicateSectionException(conf.addSection("sample"));
201- /* already exists */
202- assertThrown!DuplicateSectionException(conf.addSection("sample"));
203- }
206+ /* doesn't yet exist */
207+ assertNotThrown!DuplicateSectionException(conf.addSection("sample"));
208+ /* already exists */
209+ assertThrown!DuplicateSectionException(conf.addSection("sample"));
210+ }
204211
205- /**
206- * Indicates whether the named `section` is present in the configuration.
207- *
208- * Params:
209- * section = The section to check for in the configuration.
210- *
211- * Returns: `true` if the section exists, `false` otherwise.
212- */
213- bool hasSection(string section) @safe pure
214- {
215- auto exists = (section in m_sections);
216- return (exists !is null);
217- }
212+ /**
213+ * Indicates whether the named `section` is present in the configuration.
214+ *
215+ * Params:
216+ * section = The section to check for in the configuration.
217+ *
218+ * Returns: `true` if the section exists, `false` otherwise.
219+ */
220+ bool hasSection(string section) @safe pure const
221+ {
222+ return (section in m_sections) !is null;
223+ }
218224
219- ///
220- unittest
221- {
222- auto conf = new ConfigParser();
223- conf.addSection("nExt");
224- assert(true == conf.hasSection("nExt"), "Close the world.");
225- assert(false == conf.hasSection("world"), "Open the nExt.");
226- }
225+ ///
226+ unittest
227+ {
228+ auto conf = new ConfigParser();
229+ conf.addSection("nExt");
230+ assert(true == conf.hasSection("nExt"), "Close the world.");
231+ assert(false == conf.hasSection("world"), "Open the nExt.");
232+ }
227233
228- string[] options(string section) @safe pure
229- {
230- if (false == this.hasSection(section))
231- throw new NoSectionException(section);
232- return m_sections[section].keys();
233- }
234+ string[] options(string section) @safe pure const
235+ {
236+ if (false == this.hasSection(section))
237+ {
238+ throw new NoSectionException(section);
239+ }
240+ return m_sections[section].keys();
241+ }
234242
235- ///
236- unittest
237- {
238- import std.exception : assertNotThrown, assertThrown;
243+ ///
244+ unittest
245+ {
246+ import std.exception : assertNotThrown, assertThrown;
239247
240- auto conf = new ConfigParser();
248+ auto conf = new ConfigParser();
241249
242- conf.addSection("Settings");
250+ conf.addSection("Settings");
243251
244- assertNotThrown!NoSectionException(conf.options("Settings"));
245- assertThrown!NoSectionException(conf.options("void"));
252+ assertNotThrown!NoSectionException(conf.options("Settings"));
253+ assertThrown!NoSectionException(conf.options("void"));
246254
247- string[] options = conf.options("Settings");
248- assert(0 == options.length, "More keys than we need");
249- }
255+ string[] options = conf.options("Settings");
256+ assert(0 == options.length, "More keys than we need");
257+ }
250258
251- bool hasOption(string section, string option) @safe pure
252- {
253- import std.string : toLower;
259+ bool hasOption(string section, string option) @safe pure const
260+ {
261+ import std.string : toLower;
254262
255- if (false == this.hasSection(section))
256- return false;
263+ if (false == this.hasSection(section))
264+ {
265+ return false;
266+ }
257267
258- scope lowercaseOption = toLower(option);
259- auto exists = (lowercaseOption in m_sections[section]);
260- return (exists !is null);
261- }
262- /*
263- string[] read(string[] filenames)
264- {
265- return null;
266- }*/
268+ const lowercaseOption = toLower(option);
269+ return (lowercaseOption in m_sections[section]) !is null;
270+ }
267271
268- void read(string filename)
269- {
270- File file = File(filename, "r");
271- scope(exit) { file.close(); }
272- read(file, false);
273- }
272+ /*
273+ string[] read(string[] filenames)
274+ {
275+ return null;
276+ }*/
274277
275- ///
276- unittest
277- {
278- import std.file : remove;
279- import std.stdio : File;
280-
281- auto configFile = File("test.conf", "w+");
282- configFile.writeln("[Section 1]");
283- configFile.writeln("key=value");
284- configFile.writeln("\n[Section 2]");
285- configFile.writeln("key2 = value");
286- configFile.close();
287-
288- auto conf = new ConfigParser();
289- conf.read("test.conf");
290-
291- assert(2 == conf.sections.length, "Incorrect Sections length");
292- assert(true == conf.hasSection("Section 1"),
293- "Config file doesn't have Section 1");
294- assert(true == conf.hasOption("Section 1", "key"),
295- "Config file doesn't have 'key' in 'Section 1'");
296-
297- remove("test.conf");
298- }
278+ void read(string filename) @safe
279+ {
280+ File file = File(filename, "r");
281+ scope(exit) { file.close(); }
282+ read(file, false);
283+ }
299284
300- /**
301- * Parse a config file.
302- *
303- * Params:
304- * file = Reference to the file from which to read.
305- * close = Close the file when finished parsing.
306- */
307- void read(ref File file, bool close = true)
308- {
309- import std.array : array;
310- import std.algorithm.searching : canFind;
311- import std.string : strip;
285+ ///
286+ unittest
287+ {
288+ import std.file : remove;
289+ import std.stdio : File;
312290
313- scope(exit) { if (close) file.close(); }
291+ auto configFile = File("test.conf", "w+");
292+ configFile.writeln("[Section 1]");
293+ configFile.writeln("key=value");
294+ configFile.writeln("\n[Section 2]");
295+ configFile.writeln("key2 = value");
296+ configFile.close();
314297
315- string[] lines = file.byLineCopy.array;
298+ auto conf = new ConfigParser();
299+ conf.read("test.conf");
316300
317- for (auto i = 0; i < lines.length; i++) {
318- string line = lines[i].strip();
301+ assert(2 == conf.sections.length, "Incorrect Sections length");
302+ assert(true == conf.hasSection("Section 1"),
303+ "Config file doesn't have Section 1");
304+ assert(true == conf.hasOption("Section 1", "key"),
305+ "Config file doesn't have 'key' in 'Section 1'");
319306
320- if (line == "")
321- continue;
307+ remove("test.conf");
308+ }
322309
323- if ('[' == lines[i][0]) {
324- parseSectionHeader(lines[i]);
325- } else if (false == canFind(m_commentPrefixes, lines[i][0])) {
326- parseLine(lines[i]);
327- }
328- /* ignore comments */
329- }
330- }
310+ /**
311+ * Parse a config file.
312+ *
313+ * Params:
314+ * file = Reference to the file from which to read.
315+ * close = Close the file when finished parsing.
316+ */
317+ void read(ref File file, bool close = true) @trusted
318+ {
319+ import std.array : array;
320+ import std.algorithm.searching : canFind;
321+ import std.string : strip;
331322
332- /*void readString(string str)
333- {
334- }*/
323+ scope(exit)
324+ {
325+ if (close)
326+ {
327+ file.close();
328+ }
329+ }
335330
336- /**
337- * Get an `option` value for the named `section`.
338- *
339- * Params:
340- * section = The section to look for the given `option`.
341- * option = The option to return the value of
342- * fallback = Fallback value if the `option` is not found. Can be null.
343- *
344- * Returns:
345- * - The value for `option` if it is found.
346- * - `null` if the `option` is not found and `fallback` is not provided.
347- * - `fallback` if the `option` is not found and `fallback` is provided.
348- *
349- * Throws:
350- * - NoSectionException if the `section` does not exist and no fallback is provided.
351- * - NoOptionException if the `option` does not exist and no fallback is provided.
352- */
353- string get(string section, string option)
354- {
355- import std.string : toLower;
331+ /// XXX: Can this be change to foreach(lin; file.byLine) ?
332+ string[] lines = file.byLineCopy.array;
356333
357- scope lowercaseOption = toLower(option);
334+ for (auto i = 0; i < lines.length; i++)
335+ {
336+ string line = lines[i].strip();
358337
359- if (false == this.hasSection(section))
360- throw new NoSectionException(section);
338+ if (line == "")
339+ {
340+ continue;
341+ }
361342
362- if (false == this.hasOption(section, lowercaseOption))
363- throw new NoOptionException(section, lowercaseOption);
343+ if ('[' == lines[i][0])
344+ {
345+ parseSectionHeader(lines[i]);
346+ }
347+ else if (false == canFind(m_commentPrefixes, lines[i][0]))
348+ {
349+ parseLine(lines[i]);
350+ }
351+ /* ignore comments */
352+ }
353+ }
364354
365- return m_sections[section][lowercaseOption];
366- }
355+ /*void readString(string str)
356+ {
357+ }*/
367358
368- ///
369- unittest
370- {
371- import std.exception : assertThrown;
359+ /**
360+ * Get an `option` value for the named `section`.
361+ *
362+ * Params:
363+ * section = The section to look for the given `option`.
364+ * option = The option to return the value of
365+ * fallback = Fallback value if the `option` is not found. Can be null.
366+ *
367+ * Returns:
368+ * - The value for `option` if it is found.
369+ * - `null` if the `option` is not found and `fallback` is not provided.
370+ * - `fallback` if the `option` is not found and `fallback` is provided.
371+ *
372+ * Throws:
373+ * - NoSectionException if the `section` does not exist and no fallback is provided.
374+ * - NoOptionException if the `option` does not exist and no fallback is provided.
375+ */
376+ string get(string section, string option) @safe pure const
377+ {
378+ import std.string : toLower;
372379
373- auto conf = new ConfigParser();
374- conf.addSection("Section");
375- conf.set("Section", "option", "value");
380+ const lowercaseOption = toLower(option);
376381
377- assert(conf.get("Section", "option") == "value");
378- assertThrown!NoSectionException(conf.get("section", "option"));
379- assertThrown!NoOptionException(conf.get("Section", "void"));
380- }
382+ if (false == this.hasSection(section))
383+ {
384+ throw new NoSectionException(section);
385+ }
381386
382- /// Ditto
383- string get(string section, string option, string fallback)
384- {
385- string res = fallback;
387+ if (false == this.hasOption(section, lowercaseOption))
388+ {
389+ throw new NoOptionException(section, lowercaseOption);
390+ }
386391
387- try {
388- res = get(section, option);
389- } catch (NoSectionException e) {
390- return res;
391- } catch (NoOptionException e) {
392- return res;
393- }
392+ return m_sections[section][lowercaseOption];
393+ }
394394
395- return res;
396- }
395+ ///
396+ @safe pure unittest
397+ {
398+ import std.exception : assertThrown;
397399
398- ///
399- unittest
400- {
401- import std.exception : assertThrown;
400+ auto conf = new ConfigParser();
401+ conf.addSection("Section");
402+ conf.set("Section", "option", "value");
402403
403- auto conf = new ConfigParser();
404- conf.addSection("Section");
405- conf.set("Section", "option", "value");
404+ assert(conf.get("Section", "option") == "value");
405+ assertThrown!NoSectionException(conf.get("section", "option"));
406+ assertThrown!NoOptionException(conf.get("Section", "void"));
407+ }
406408
407- assert("value" == conf.get("Section", "option"));
408- assert("fallback" == conf.get("section", "option", "fallback"));
409- assert("fallback" == conf.get("Section", "void", "fallback"));
409+ /// Ditto
410+ string get(string section, string option, string fallback) @safe pure const
411+ {
412+ try
413+ {
414+ return get(section, option);
415+ }
416+ catch (NoSectionException e)
417+ {
418+ return fallback;
419+ }
420+ catch (NoOptionException e)
421+ {
422+ return fallback;
423+ }
424+ }
410425
411- /* can use null for fallback */
412- assert(null == conf.get("section", "option", null));
413- assert(null == conf.get("Section", "void", null));
414- }
426+ ///
427+ @safe pure unittest
428+ {
429+ import std.exception : assertThrown;
415430
416- /**
417- * A convenience method which casts the value of `option` in `section`
418- * to an integer.
419- *
420- * Params:
421- * section = The section to look for the given `option`.
422- * option = The option to return the value for.
423- * fallback = The fallback value to use if `option` isn't found.
424- *
425- * Returns:
426- *
427- *
428- * Throws:
429- * - NoSectionFoundException if `section` doesn't exist.
430- * - NoOptionFoundException if the `section` doesn't contain `option`.
431- * - ConvException if it failed to parse the value to an int.
432- * - ConvOverflowException if the value would overflow an int.
433- *
434- * See_Also: get()
435- */
436- int getInt(string section, string option)
437- {
438- import std.conv : parse;
431+ auto conf = new ConfigParser();
432+ conf.addSection("Section");
433+ conf.set("Section", "option", "value");
439434
440- string res;
435+ assert("value" == conf.get("Section", "option"));
436+ assert("fallback" == conf.get("section", "option", "fallback"));
437+ assert("fallback" == conf.get("Section", "void", "fallback"));
441438
442- res = get(section, option);
439+ /* can use null for fallback */
440+ assert(null is conf.get("section", "option", null));
441+ assert(null is conf.get("Section", "void", null));
442+ }
443443
444- return parse!int(res);
445- }
444+ /**
445+ * A convenience method which casts the value of `option` in `section`
446+ * to an integer.
447+ *
448+ * Params:
449+ * section = The section to look for the given `option`.
450+ * option = The option to return the value for.
451+ * fallback = The fallback value to use if `option` isn't found.
452+ *
453+ * Returns:
454+ *
455+ *
456+ * Throws:
457+ * - NoSectionFoundException if `section` doesn't exist.
458+ * - NoOptionFoundException if the `section` doesn't contain `option`.
459+ * - ConvException if it failed to parse the value to an int.
460+ * - ConvOverflowException if the value would overflow an int.
461+ *
462+ * See_Also: get()
463+ */
464+ int getInt(string section, string option) @safe pure const
465+ {
466+ import std.conv : parse;
446467
447- /// Ditto
448- int getInt(string section, string option, int fallback)
449- {
450- int res = fallback;
468+ auto res = get(section, option);
451469
452- try {
453- res = getInt(section, option);
454- } catch (Exception e) {
455- return res;
456- }
470+ return parse!int(res);
471+ }
457472
458- return res;
459- }
473+ /// Ditto
474+ int getInt(string section, string option, int fallback) @safe pure const
475+ {
476+ try
477+ {
478+ return getInt(section, option);
479+ }
480+ catch (NoSectionException nse)
481+ {
482+ return fallback;
483+ }
484+ catch (NoOptionException noe)
485+ {
486+ return fallback;
487+ }
488+ catch (ConvException ce)
489+ {
490+ return fallback;
491+ }
492+ }
460493
461494 /*
462495 double getDouble(string section, string option)
@@ -475,217 +508,234 @@ public class ConfigParser
475508 {
476509 }*/
477510
478- /**
479- * A convenience method which coerces the $(I option) in the
480- * specified $(I section) to a boolean value.
481- *
482- * Note that the accepted values for the option are "1", "yes",
483- * "true", and "on", which cause this method to return `true`, and
484- * "0", "no", "false", and "off", which cause it to return `false`.
485- *
486- * These string values are checked in a case-insensitive manner.
487- *
488- * Params:
489- * section = The section to look for the given option.
490- * option = The option to return the value for.
491- * fallback = The fallback value to use if the option was not found.
492- *
493- * Throws:
494- * - NoSectionFoundException if `section` doesn't exist.
495- * - NoOptionFoundException if the `section` doesn't contain `option`.
496- * - ConvException if any other value was found.
497- */
498- bool getBool(string section, string option)
499- {
500- import std.string : toLower;
501-
502- string value = get(section, option);
503-
504- switch (value.toLower)
505- {
506- case "1":
507- case "yes":
508- case "true":
509- case "on":
510- return true;
511- case "0":
512- case "no":
513- case "false":
514- case "off":
515- return false;
516- default:
517- throw new ConvException("No valid boolean value found");
518- }
519- }
511+ /**
512+ * A convenience method which coerces the $(I option) in the
513+ * specified $(I section) to a boolean value.
514+ *
515+ * Note that the accepted values for the option are "1", "yes",
516+ * "true", and "on", which cause this method to return `true`, and
517+ * "0", "no", "false", and "off", which cause it to return `false`.
518+ *
519+ * These string values are checked in a case-insensitive manner.
520+ *
521+ * Params:
522+ * section = The section to look for the given option.
523+ * option = The option to return the value for.
524+ * fallback = The fallback value to use if the option was not found.
525+ *
526+ * Throws:
527+ * - NoSectionFoundException if `section` doesn't exist.
528+ * - NoOptionFoundException if the `section` doesn't contain `option`.
529+ * - ConvException if any other value was found.
530+ */
531+ bool getBool(string section, string option) @safe pure const
532+ {
533+ import std.string : toLower;
520534
521- /// Ditto
522- bool getBool(string section, string option, bool fallback)
523- {
524- try {
525- return getBool(section, option);
526- } catch (Exception e) {
527- return fallback;
528- }
529- }
535+ const value = get(section, option).toLower;
536+
537+ switch (value)
538+ {
539+ case "1":
540+ case "yes":
541+ case "true":
542+ case "on":
543+ return true;
544+ case "0":
545+ case "no":
546+ case "false":
547+ case "off":
548+ return false;
549+ default:
550+ throw new ConvException("No valid boolean value found");
551+ }
552+ }
553+
554+ /// Ditto
555+ bool getBool(string section, string option, bool fallback) @safe pure const
556+ {
557+ try
558+ {
559+ return getBool(section, option);
560+ }
561+ catch (NoSectionException e)
562+ {
563+ return fallback;
564+ }
565+ catch (NoOptionException e)
566+ {
567+ return fallback;
568+ }
569+ catch (ConvException e)
570+ {
571+ return fallback;
572+ }
573+ }
530574
531575 /*
532576 string[string] items(string section)
533577 {
534578 }*/
535579
536- /**
537- * Remove the specified `option` from the specified `section`.
538- *
539- * Params:
540- * section = The section to remove from.
541- * option = The option to remove from section.
542- *
543- * Retruns:
544- * `true` if option existed, false otherwise.
545- *
546- * Throws:
547- * - NoSectionException if the specified section doesn't exist.
548- */
549- bool removeOption(string section, string option)
550- {
551- if ((section in m_sections) is null) {
552- throw new NoSectionException(section);
553- }
580+ /**
581+ * Remove the specified `option` from the specified `section`.
582+ *
583+ * Params:
584+ * section = The section to remove from.
585+ * option = The option to remove from section.
586+ *
587+ * Retruns:
588+ * `true` if option existed, false otherwise.
589+ *
590+ * Throws:
591+ * - NoSectionException if the specified section doesn't exist.
592+ */
593+ bool removeOption(string section, string option) @safe pure
594+ {
595+ if ((section in m_sections) is null)
596+ {
597+ throw new NoSectionException(section);
598+ }
554599
555- if (option in m_sections[section]) {
556- m_sections[section].remove(option);
557- return true;
558- }
600+ if (option in m_sections[section])
601+ {
602+ m_sections[section].remove(option);
603+ return true;
604+ }
559605
560- return false;
561- }
606+ return false;
607+ }
562608
563- ///
564- unittest
565- {
566- import std.exception : assertThrown;
609+ ///
610+ @safe pure unittest
611+ {
612+ import std.exception : assertThrown;
567613
568- auto conf = new ConfigParser();
569- conf.addSection("Default");
570- conf.set("Default", "exists", "true");
614+ auto conf = new ConfigParser();
615+ conf.addSection("Default");
616+ conf.set("Default", "exists", "true");
571617
572- assertThrown!NoSectionException(conf.removeOption("void", "false"));
573- assert(false == conf.removeOption("Default", "void"));
574- assert(true == conf.removeOption("Default", "exists"));
575- }
618+ assertThrown!NoSectionException(conf.removeOption("void", "false"));
619+ assert(false == conf.removeOption("Default", "void"));
620+ assert(true == conf.removeOption("Default", "exists"));
621+ }
576622
577- /**
578- * Remove the specified `section` from the config.
579- *
580- * Params:
581- * section = The section to remove.
582- *
583- * Returns:
584- * `true` if the section existed, `false` otherwise.
585- */
586- bool removeSection(string section)
587- {
588- if (section in m_sections) {
589- m_sections.remove(section);
590- return true;
591- }
592- return false;
593- }
623+ /**
624+ * Remove the specified `section` from the config.
625+ *
626+ * Params:
627+ * section = The section to remove.
628+ *
629+ * Returns:
630+ * `true` if the section existed, `false` otherwise.
631+ */
632+ bool removeSection(string section) @safe pure
633+ {
634+ if (section in m_sections)
635+ {
636+ m_sections.remove(section);
637+ return true;
638+ }
639+ return false;
640+ }
594641
595- ///
596- unittest
597- {
598- auto conf = new ConfigParser();
599- conf.addSection("Exists");
600- assert(false == conf.removeSection("DoesNotExist"));
601- assert(true == conf.removeSection("Exists"));
602- }
642+ ///
643+ @safe pure unittest
644+ {
645+ auto conf = new ConfigParser();
646+ conf.addSection("Exists");
647+ assert(false == conf.removeSection("DoesNotExist"));
648+ assert(true == conf.removeSection("Exists"));
649+ }
603650
604- void set(string section, string option, string value) @safe pure
605- {
606- import std.string : toLower;
651+ void set(string section, string option, string value) @safe pure
652+ {
653+ import std.string : toLower;
654+
655+ if (false == this.hasSection(section))
656+ {
657+ throw new NoSectionException(section);
658+ }
607659
608- if (false == this.hasSection(section))
609- throw new NoSectionException(section);
660+ const lowercaseOption = toLower(option);
661+ m_sections[section][lowercaseOption] = value;
662+ }
610663
611- scope lowercaseOption = toLower(option);
612- m_sections[section][lowercaseOption] = value;
613- }
664+ ///
665+ @safe pure unittest
666+ {
667+ import std.exception : assertThrown;
614668
615- ///
616- unittest
617- {
618- import std.exception : assertThrown;
669+ auto conf = new ConfigParser();
619670
620- auto conf = new ConfigParser();
671+ assertThrown!NoSectionException(conf.set("Section", "option", "value"));
621672
622- assertThrown!NoSectionException(conf.set("Section", "option",
623- "value"));
673+ conf.addSection("Section");
674+ conf.set("Section", "option", "value");
675+ assert(conf.get("Section", "option") == "value");
676+ }
624677
625- conf.addSection("Section");
626- conf.set("Section", "option", "value");
627- assert(conf.get("Section", "option") == "value");
628- }
678+ ///
679+ /// Write a representation of the configuration to the
680+ /// provided *file*.
681+ ///
682+ /// This representation can be parsed by future calls to
683+ /// `read`. This does **not** close the file after writing.
684+ ///
685+ /// Params:
686+ /// file = An open file which was opened in text mode.
687+ /// spaceAroundDelimiters = The delimiters between keys and
688+ /// values are surrounded by spaces.
689+ ///
690+ /// Note: Comments from the original file are not preserved when
691+ /// writing the configuration back.
692+ ///
693+ void write(ref File file, bool spaceAroundDelimiters = true) @safe const
694+ {
695+ const del = spaceAroundDelimiters ? " = " : "=";
629696
630- ///
631- /// Write a representation of the configuration to the
632- /// provided *file*.
633- ///
634- /// This representation can be parsed by future calls to
635- /// `read`. This does **not** close the file after writing.
636- ///
637- /// Params:
638- /// file = An open file which was opened in text mode.
639- /// spaceAroundDelimiters = The delimiters between keys and
640- /// values are surrounded by spaces.
641- ///
642- /// Note: Comments from the original file are not preserved when
643- /// writing the configuration back.
644- ///
645- void write(ref File file, bool spaceAroundDelimiters = true)
646- {
647- string del = spaceAroundDelimiters ? " = " : "=";
648-
649- foreach(string section, string[string] options; m_sections) {
650- file.writefln("[%s]", section);
651-
652- foreach(string option, string value; options) {
653- file.writefln("%s%s%s", option, del, value);
654- }
655- }
656- }
697+ foreach(const section, const options; m_sections)
698+ {
699+ file.writefln("[%s]", section);
657700
658- ///
659- unittest
660- {
661- import std.file : remove;
662- import std.stdio : File;
701+ foreach(const option, const value; options)
702+ {
703+ file.writefln("%s%s%s", option, del, value);
704+ }
705+ }
706+ }
663707
664- auto writer = new ConfigParser();
665- writer.addSection("general");
708+ ///
709+ @safe unittest
710+ {
711+ import std.file : remove;
712+ import std.stdio : File;
666713
667- writer.addSection("GUI");
668- writer.set("GUI", "WINDOW_WIDTH", "848");
669- writer.set("GUI", "WINDOW_HEIGHT", "480");
714+ auto writer = new ConfigParser();
715+ writer.addSection("general");
670716
671- auto file = File("test.ini", "w+");
672- scope(exit) remove(file.name);
673- writer.write(file);
717+ writer.addSection("GUI");
718+ writer.set("GUI", "WINDOW_WIDTH", "848");
719+ writer.set("GUI", "WINDOW_HEIGHT", "480");
674720
675- file.rewind();
721+ auto file = File("test.ini", "w+");
722+ scope(exit) remove(file.name);
723+ writer.write(file);
676724
677- auto reader = new ConfigParser();
678- reader.read(file);
725+ file.rewind();
679726
680- assert(reader.hasSection("general"), "reader does not contain general section");
727+ auto reader = new ConfigParser();
728+ reader.read(file);
681729
682- assert(reader.hasSection("GUI"), "reader does not contain GUI section");
683- assert(reader.get("GUI", "WINDOW_WIDTH") == "848", "reader GUI.WINDOW_WIDTH is not 848");
684- assert(reader.getInt("GUI", "WINDOW_WIDTH") == 848, "reader GUI.WINDOW_WIDTH is not 848 (int)");
730+ assert(reader.hasSection("general"), "reader does not contain general section");
685731
686- assert(reader.get("GUI", "WINDOW_HEIGHT") == "480", "reader GUI.WINDOW_HEIGHT is not 480");
687- assert(reader.getInt("GUI", "WINDOW_HEIGHT") == 480, "reader GUI.WINDOW_HEIGHT is not 480 (int)");
688- }
732+ assert(reader.hasSection("GUI"), "reader does not contain GUI section");
733+ assert(reader.get("GUI", "WINDOW_WIDTH") == "848", "reader GUI.WINDOW_WIDTH is not 848");
734+ assert(reader.getInt("GUI", "WINDOW_WIDTH") == 848, "reader GUI.WINDOW_WIDTH is not 848 (int)");
735+
736+ assert(reader.get("GUI", "WINDOW_HEIGHT") == "480", "reader GUI.WINDOW_HEIGHT is not 480");
737+ assert(reader.getInt("GUI", "WINDOW_HEIGHT") == 480, "reader GUI.WINDOW_HEIGHT is not 480 (int)");
738+ }
689739
690740 ///
691741 /// Write a representation of the configuration to the
@@ -702,14 +752,14 @@ public class ConfigParser
702752 /// Note: Comments from the original file are not preserved when
703753 /// writing the configuration back.
704754 ///
705- void write(string filename, bool spaceAroundDelimiters = true)
755+ void write(string filename, bool spaceAroundDelimiters = true) @safe const
706756 {
707757 auto file = File(filename, "w+");
708758 write(file, spaceAroundDelimiters);
709759 }
710760
711761 ///
712- unittest
762+ @safe unittest
713763 {
714764 import std.file : remove;
715765
@@ -750,24 +800,25 @@ public class ConfigParser
750800 /// Note: Comments from the original file are not preserved when
751801 /// writing the configuration back.
752802 ///
753- void write(T)(T buffer, bool spaceAroundDelimiters = true) @safe pure
803+ void write(T)(T buffer, bool spaceAroundDelimiters = true) @safe pure const
754804 if (isOutputRange!(T, string))
755805 {
756806 import std.format : format;
757807
758808 const del = spaceAroundDelimiters ? " = " : "=";
759809
760- foreach(string section, string[string] options; m_sections)
810+ foreach(const section, const options; m_sections)
761811 {
762812 buffer.put(format!"[%s]\n"(section));
763813
764- foreach(string option, string value; options)
814+ foreach(const option, const value; options)
765815 {
766816 buffer.put(format!"%s%s%s\n"(option, del, value));
767817 }
768818 }
769819 }
770820
821+ ///
771822 @safe pure unittest
772823 {
773824 import std.outbuffer : OutBuffer;
@@ -792,86 +843,65 @@ public class ConfigParser
792843 }
793844 }
794845
795- private:
796-
797- void parseSectionHeader(ref string line)
798- {
799- import std.array : appender, assocArray;
800-
801- auto sectionHeader = appender!string;
802- /* presume that the last character is ] */
803- sectionHeader.reserve(line.length - 1);
804- string popped = line[1 .. $];
805-
806- foreach(c; popped) {
807- if (c != ']')
808- sectionHeader.put(c);
809- else
810- break;
811- }
812-
813- m_currentSection = sectionHeader.data();
846+private:
814847
815- if (m_currentSection in m_sections && m_strict)
816- throw new DuplicateSectionException(m_currentSection);
817-
818- try {
819- this.addSection(m_currentSection);
820- } catch (DuplicateSectionException) {
821- }
822- }
823-
824- void parseLine(ref string line)
825- {
826- import std.string : indexOfAny, toLower, strip;
827-
828- ptrdiff_t idx = line.indexOfAny(m_delimiters);
829- if (-1 == idx) return;
830- string option = line[0 .. idx].dup.strip.toLower;
831- string value = line[idx + 1 .. $].dup.strip;
848+ void parseSectionHeader(const ref string line) @safe pure
849+ {
850+ import std.array : appender, assocArray;
832851
833- if (option in m_sections[m_currentSection] && m_strict)
834- throw new DuplicateOptionException(option, m_currentSection);
852+ auto sectionHeader = appender!string;
853+ /* presume that the last character is ] */
854+ sectionHeader.reserve(line.length - 1);
855+ string popped = line[1 .. $];
835856
836- m_sections[m_currentSection][option] = value;
837- }
857+ foreach(const c; popped)
858+ {
859+ if (c != ']')
860+ {
861+ sectionHeader.put(c);
862+ }
863+ else
864+ {
865+ break;
866+ }
867+ }
838868
839- unittest
840- {
841- import std.exception : assertThrown, assertNotThrown;
842- import std.file : remove;
869+ m_currentSection = sectionHeader.data();
843870
844- auto f = File("config.cfg", "w+");
845- f.writeln("[section]");
846- f.writeln("option = value");
847- f.writeln("Option = value");
848- f.close();
849- scope(exit) remove("config.cfg");
871+ if (m_currentSection in m_sections && m_strict)
872+ {
873+ throw new DuplicateSectionException(m_currentSection);
874+ }
850875
851- // Duplicate option
852- scope parser = new ConfigParser();
853- assertThrown!DuplicateOptionException(parser.read("config.cfg"));
876+ try
877+ {
878+ this.addSection(m_currentSection);
879+ }
880+ catch (DuplicateSectionException)
881+ {
882+ /* no-op - just making sure the section exists. */
883+ }
884+ }
854885
855- // Duplicate section
856- f = File("config.cfg", "w+");
857- f.writeln("[section]");
858- f.writeln("option = value");
859- f.writeln("[section]");
860- f.close();
886+ void parseLine(const ref string line) @safe pure
887+ {
888+ import std.string : indexOfAny, toLower, strip;
861889
862- assertThrown!DuplicateSectionException(parser.read("config.cfg"));
890+ const idx = line.indexOfAny(m_delimiters);
863891
864- // not strict
865- scope relaxedParser = new ConfigParser(['='], [], false);
892+ if (-1 == idx)
893+ {
894+ return;
895+ }
866896
867- assertNotThrown!DuplicateSectionException(relaxedParser.read("config.cfg"));
868- assert(relaxedParser.hasSection("section"));
897+ const option = line[0 .. idx].dup.strip.toLower;
898+ const string value = line[idx + 1 .. $].dup.strip;
869899
870- f = File("config.cfg", "a+");
871- f.writeln("option = newValue");
872- f.close();
900+ if (option in m_sections[m_currentSection] && m_strict)
901+ {
902+ throw new DuplicateOptionException(option, m_currentSection);
903+ }
873904
874- assertNotThrown!DuplicateOptionException(relaxedParser.read("config.cfg"));
875- assert(relaxedParser.get("section", "option") == "newValue");
876- }
905+ m_sections[m_currentSection][option] = value;
906+ }
877907 }