1 /**
2  * Container for configurations data
3  *
4  * Copyright: © 2014-2021
5  * License: MIT license. License terms written in licence.txt file
6  *
7  * Authors: Oleg Nykytenko, oleg.nykytenko@gmail.com
8  *
9  * Version: 1.xx Date: 11.02.2014
10  * Version: 2.xx Date: 25.10.2015
11  */
12 
13 module onyx.bundle;
14 
15 import std.traits;
16 import std.array;
17 import std.algorithm;
18 
19 @safe:
20 
21 /************************************************************************************/
22 /* Bundle element types                                                             */
23 /************************************************************************************/
24 
25 /**
26  * Values - save one set of data in array.
27  *
28  * string[] - data array
29  */
30 alias string[] Values;
31 
32 /**
33  * Key - string key for Values
34  */
35 alias string Key;
36 
37 /**
38  * GlValue - Bundle global data group
39  */
40 alias Values[Key] GlValue;
41 
42 /**
43  * GlKey - string key in Bundle for data group
44  */
45 alias string GlKey;
46 
47 
48 /**
49  * Configuration exception
50  */
51 class BundleException:Exception
52 {
53     @safe
54     this(string exString) pure nothrow
55     {
56         super(exString);
57     }
58 }
59 
60 
61 /**
62  * Construct exception from parrent: ConfException
63  *
64  */
65 template childBundleException(string exceptionName)
66 {
67     const char[] childBundleException =
68 
69     "class " ~exceptionName~":BundleException
70     {
71         @safe
72         this(string exString) pure nothrow
73         {
74             super(exString);
75         }
76     }";
77 }
78 
79 
80 mixin(childBundleException!"GlKeyNotFoundException");
81 mixin(childBundleException!"KeyNotFoundException");
82 mixin(childBundleException!"ValuesNotFoundException");
83 mixin(childBundleException!"ValueNotFoundException");
84 
85 
86 
87 /************************************************************************************/
88 /* Parameters for parsing and building bundle                                       */
89 /************************************************************************************/
90 struct Parameters
91 {
92     /*
93      * Start and End Global key symbols
94      */
95     immutable string startGlKeySymbol = "[";
96     immutable string endGlKeySymbol = "]";
97 
98     /*
99      * Separator symbol between "Key" and "Values"
100      */
101     immutable string[] keySeparators = ["="];
102 
103     /*
104      *  Comment symbol
105      */
106     immutable string commentSymbol = "#";
107 
108     /**
109      * Create parameters
110      */
111     immutable nothrow pure
112     this(immutable string startGlKeySymbol,
113         immutable string endGlKeySymbol,
114         immutable string[] keySeparators,
115         immutable string commentSymbol)
116     {
117         this.startGlKeySymbol = startGlKeySymbol;
118         this.endGlKeySymbol = endGlKeySymbol;
119         this.keySeparators = keySeparators;
120         this.commentSymbol = commentSymbol;
121     }
122 }
123 
124 
125 
126 unittest
127 {
128     auto parameters = immutable Parameters("<", ")", ["=", " ", "->"], "#");
129 }
130 
131 
132 
133 /************************************************************************************/
134 /* Data container                                                                   */
135 /************************************************************************************/
136 /**
137  * Bundle save data in container (associative array GlValue[GlKey]).
138  *
139  * Container structure:
140  *      GlKey1 -> GlValue1
141  *      GlKey2 -> GlValue2
142  *      GlKey3 -> GlValue3
143  *  ..................
144  *      GlKeyN -> GlValueN
145  *                      |
146  *                      |=> Key1 -> Values1
147  *                          Key2 -> Values2
148  *                          ..............
149  *                          KeyM -> ValuesM
150  *                                      |
151  *                                      |=> [DataValue1, DataValue2, ... DataValueP]
152  */
153 class Bundle
154 {
155     /*
156      * Data inner container
157      */
158     private GlValue[GlKey] container;
159 
160     /**
161      * Build Bundle from text file
162      *
163      * Throws: BundleException, GlKeyNotFoundException, KeyNotFoundException, ValuesNotFoundException, Exception
164      */
165     @trusted
166     this(string filePath, immutable Parameters pars = immutable Parameters()) immutable
167     {
168         auto lines = copyFileToStrings(filePath);
169         this(lines, pars);
170     }
171 
172     /**
173      * Build Bundle from string array
174      *
175      * Throws: BundleException, GlKeyNotFoundException, KeyNotFoundException, ValuesNotFoundException, Exception
176      */
177     this(string[] lines, immutable Parameters pars = immutable Parameters()) immutable
178     {
179         string[int] nlines;
180         int index = 0;
181         foreach(line; lines)	nlines[++index] = line;
182         this(nlines, pars);
183     }
184 
185     /**
186      * Build Bundle from string array
187      *
188      * Throws: BundleException, GlKeyNotFoundException, KeyNotFoundException, ValuesNotFoundException, Exception
189      */
190     @trusted
191     this(string[int] lines, immutable Parameters pars = immutable Parameters()) immutable
192     {
193     	container = buildContainer(lines, pars);
194     }
195 
196     /**
197      * Build Bundle from Bundle
198      */
199     private this(immutable GlValue[GlKey] c) nothrow pure immutable
200     {
201     	container = c;
202     }
203 
204     /**
205      * Get Global value from bundle
206      *
207      * Returns: GlValue - content of one bundle block
208      *
209      * Throws: GlKeyNotFoundException, BundleException
210      */
211     immutable (GlValue) glValue(GlKey glKey) pure immutable
212     {
213         if (glKey is null)
214         {
215             throw new BundleException("Global Key is null");
216         }
217         if (glKey !in container)
218         {
219             throw new GlKeyNotFoundException("Not found Global Key: ["~glKey~"]");
220         }
221         return container[glKey];
222     }
223 
224     /**
225      * Get Values from bundle
226      *
227      * Returns: Values - content of one bundle line
228      *
229      * Throws: ValuesNotFoundException, KeyNotFoundException, GlKeyNotFoundException, BundleException
230      */
231     immutable (Values) values(GlKey glKey, Key key) pure immutable
232     {
233         if (key is null)
234         {
235             throw new BundleException("Key is null");
236         }
237         immutable glV = glValue(glKey);
238         if (key !in glV)
239         {
240             throw new KeyNotFoundException("Not found Key: ["~glKey~"] -> "~key);
241         }
242         immutable vs = glV[key];
243         if ((vs is null) || (vs.length == 0))
244         {
245             throw new ValuesNotFoundException("For key: ["~glKey~"] -> "~key~" values not found");
246         }
247         return vs;
248     }
249 
250     /**
251      * Get one value from bundle
252      *
253      * Returns: Value - one string value from bundle line
254      *
255      * Throws: ValueNotFoundException, KeyNotFoundException, GlKeyNotFoundException, BundleException
256      */
257     @trusted
258     immutable (T) value(T=string)(GlKey glKey, Key key, uint pos) pure immutable
259     {
260         string vExceptionMsg = "Not found value: ["~glKey~"] -> "~key~" = values["~to!string(pos)~"]";
261         try
262         {
263             immutable tValues = values(glKey, key);
264             if ((tValues.length <= pos) || (tValues[pos] is null))
265             {
266                 throw new ValueNotFoundException(vExceptionMsg);
267             }
268 
269             static if(is(T == bool))
270                 return to!bool(tValues[pos]);
271             else
272                 return strToNum!T(tValues[pos]);
273         }
274         catch (ValuesNotFoundException vse)
275         {
276             throw new ValueNotFoundException(vExceptionMsg);
277         }
278     }
279 
280     /**
281      * Get one value from bundle
282      *
283      * Returns: Value - first string value from bundle line
284      *
285      * Throws: ValueNotFoundException, KeyNotFoundException, GlKeyNotFoundException, ConfException
286      */
287     immutable (T) value(T=string)(GlKey glKey, Key key) pure immutable
288     {
289         return value!T(glKey, key, 0);
290     }
291 
292     /**
293      * Check if value present in bundle (pos value from line)
294      *
295      * Returns: if value present in bundle - true, else - false
296      */
297     bool isValuePresent(GlKey glKey, Key key, int pos) nothrow pure immutable
298     {
299         try
300         {
301             value(glKey, key, pos);
302             return true;
303         }
304         catch(Exception e)
305         {
306             return false;
307         }
308     }
309 
310     /**
311      * Check is value present in bundle (First value from line)
312      *
313      * Returns: if value present in bundle - true, else - false
314      */
315     bool isValuePresent(GlKey glKey, Key key) pure immutable nothrow
316     {
317         return isValuePresent(glKey, key, 0);
318     }
319 
320     /**
321      * Check is global key present in bundle
322      *
323      * Returns: if key present in bundle - true, else - false
324      */
325     bool isGlKeyPresent(GlKey glKey) immutable pure nothrow
326     {
327         return ((glKey in container)==null)?false:true;
328     }
329 
330     /**
331      * Get global keys present in bundle
332      *
333      * Returns: GlKey array
334      */
335     @trusted /* array.keys is system */
336     immutable (GlKey[]) glKeys() immutable pure nothrow
337     {
338         return container.keys;
339     }
340 
341     /**
342      * Get keys present in bundle at one global key
343      *
344      * Returns: Key array
345      */
346     @trusted /* array.keys is system */
347     immutable (Key[]) keys(GlKey glKey) immutable pure nothrow
348     {
349         return container[glKey].keys;
350     }
351 
352     /**
353      * Add two bundles (this and rBundle)
354      *
355      * Returns: New Configuration bundle with data from this bundle and rBundle bundle
356      *
357      * Throws: ConfException
358      *
359      * Example:
360      * auto bundle1 = ConfBundle("./conf1/receiver1.conf");
361      * auto bundle2 = ConfBundle(confArray);
362      * auto bundle = bundle1 + bundle2;
363      */
364     @trusted
365     immutable (Bundle) opBinary(string op)(immutable Bundle rBundle) immutable if (op == "+")
366     {
367         auto mutc = Bundle.dup(this);
368         auto mutcr = Bundle.dup(rBundle);
369 
370         foreach (glKey; mutcr.keys)
371         {
372             auto rGlValue = mutcr[glKey];
373             if (glKey !in mutc)
374                     mutc[glKey] = rGlValue;
375             else
376             {
377                 auto lGlValue = mutc[glKey];
378                 foreach (key; rGlValue.keys)
379                 {
380                     if (key !in mutc[glKey])
381                     {
382                     	lGlValue[key] = rGlValue[key];
383                     }
384                 }
385             }
386         }
387         import std.exception;
388         return new immutable Bundle(assumeUnique(mutc));
389     }
390 
391     /**
392      * Get from this bundle one global data part
393      *
394      * Returns: New bundle with one global part data
395      */
396     immutable (Bundle) subBundle(GlKey glKey) immutable
397     {
398         return new immutable Bundle([glKey:container[glKey]]);
399     }
400 
401 	/**
402 	 * Make muttable copy of bundle container
403 	 *
404 	 * Returns: bundle container
405 	 */
406     @trusted
407     static GlValue[GlKey] dup(immutable Bundle bundle)
408     {
409         GlValue[GlKey] mutc;
410         foreach (glKey; bundle.container.keys)
411         {
412             Values[Key] mutglValue;
413 
414             auto glValue = bundle.container[glKey];
415 
416             foreach(key; glValue.keys)
417             {
418                 mutglValue[key] = glValue[key].dup;
419             }
420             mutc[glKey] = mutglValue;
421             mutglValue = null;
422         }
423         return mutc;
424     }
425 
426 }
427 
428 
429 /************************************************************************************/
430 /* Lib tests                                                                        */
431 /************************************************************************************/
432 unittest
433 {
434     string[] s =
435         ["[general] 			# GlKey = general",
436          "module_name = Main 	# Key = module_name, Values[0] = Main, keySeparator = EQUALS SIGN",
437          "[DebugLogger]			# GlKey = DebugLogger",
438          "level = debug",
439          "appender = FileAppender",
440          "rolling = SizeBasedRollover",
441          "fileName = ./log/MainDebug.log",
442          "[empty_gl_key]", // Test GlKey with empty GlValue
443          "[data_receive]",
444          "# Key = 0xC000, Values[0] = 0x014B, Values[1] = 0x0B, keySeparator = SPACE",
445          "0xC000 		0x014B		0x0B		Рstation	yes		1		(32*{0xC000}+0)"];
446 
447     auto p = immutable Parameters("[", "]", ["=", " "], "#");
448 
449     auto bundle = new immutable Bundle(s, p);
450 
451 
452     // glValue test
453     {
454         auto glValue = bundle.glValue("general");
455         immutable gv = ["module_name":["Main"]];
456         assert (glValue == gv);
457     }
458 
459     // Emty GlValue test
460     {
461         auto glValue = bundle.glValue("empty_gl_key");
462         assert (glValue == null);
463     }
464 
465     // values test
466     {
467         auto values = bundle.values("DebugLogger", "level");
468         assert (values == ["debug"]);
469 
470     }
471 
472     // Value test (N pos)
473     {
474         auto value = bundle.value("data_receive", "0xC000", 3);
475         assert (value == "yes");
476     }
477 
478     // Value test (0 pos)
479     {
480         auto value = bundle.value("data_receive", "0xC000");
481         assert (value == "0x014B");
482     }
483 
484     // int Value test (0 pos)
485     {
486         auto value = bundle.value!int("data_receive", "0xC000");
487         assert (value == 0x014B);
488     }
489 
490     // isValuePresent test
491     {
492         auto present = bundle.isValuePresent("data_receive", "0xC000", 1);
493         assert (present == true);
494         auto nopresent = bundle.isValuePresent("general", "module_name", 5);
495         assert (nopresent == false);
496     }
497 
498     // isGlKeyPresent
499     {
500         auto present = bundle.isGlKeyPresent("DebugLogger");
501         assert (present == true);
502         auto nopresent = bundle.isGlKeyPresent("superkey");
503         assert (nopresent == false);
504     }
505 }
506 
507 
508 unittest
509 {
510     version(vTest)
511     {
512         auto bundle = new immutable Bundle("./test/simple.conf");
513 
514         auto user = bundle.value("config", "user");
515         assert (user == "Mark");
516 
517         auto connecting = bundle.value!bool("config", "connecting");
518         assert (connecting == true);
519 
520         auto timeout = bundle.value!int("config", "timeout");
521         assert (timeout == 2000);
522 
523         auto tmax = bundle.value!double("config", "tmax");
524         assert (tmax == 26.7);
525     }
526 }
527 
528 
529 unittest
530 {
531     version(vTest)
532     {
533         auto parameters = immutable Parameters("[", "]", ["=", "->"], "#");
534 
535         auto bundle = new immutable Bundle("./test/test.conf", parameters);
536 
537         // getGlValue test
538         {
539             auto glValue = bundle.glValue("general");
540             immutable gv = ["mod_name":["KPR"], "mod_type":["RptR11Transceiver"]];
541             assert (glValue == gv);
542         }
543 
544         // getValues test
545         {
546             auto values = bundle.values("protocol", "channel_switch_timeout");
547             assert (values == ["1000", "100", "10"]);
548         }
549 
550         /* get value for line with many values from possition 1 */
551         {
552             auto value = bundle.value("protocol", "channel_switch_timeout", 1);
553             assert (value == "100");
554         }
555 
556         // getValue test (N pos)
557         {
558             auto value = bundle.value("data_receive", "0xC000~1", 5);
559             assert (value == "(1*{0xC000}+0)");
560         }
561 
562         // getValue test (0 pos)
563         {
564             auto value = bundle.value!int("data_receive", "0xC179");
565             assert (value == 0xC179);
566         }
567 
568 
569         /* Build another bundle from string array */
570         string[] s2 =
571            ["[protocol]",
572            "data_flow = input",
573            "[new_gl_key]",
574            "test_key = value1 value2"];
575 
576         /* Create bundle from string array */
577         auto bundle2 = new immutable Bundle(s2);
578 
579         /* Add two bundles. Created new bundle with data from both bundles */
580         auto newBundle = bundle + bundle2;
581         auto val5 = newBundle.value("general", "mod_name");
582         assert (val5 == "KPR");
583         auto val6 = newBundle.value("new_gl_key", "test_key", 1);
584         assert (val6 == "value2");
585 
586         /* Get from bundle one global data part (in example with global key: "protocol") */
587         auto partBundle = newBundle.subBundle("protocol");
588 
589     }
590 }
591 
592 
593 unittest
594 {
595     string[] s;
596 
597     s ~= ["[general]"];
598     s ~= ["module_name = Main"];
599     s ~= ["[DebugLogger]"];
600     s ~= ["level = debug"];
601     s ~= ["appender = FileAppender"];
602 
603     auto p = immutable Parameters("[", "]", ["="], "#");
604 
605     auto bundle = new immutable Bundle(s, p);
606 
607     GlValue[GlKey] mutc = Bundle.dup(bundle);
608 
609     assert(bundle.container["general"]["module_name"] == ["Main"]);
610     assert(mutc["general"]["module_name"] == ["Main"]);
611 
612     mutc["general"]["module_name"] = ["Two"];
613 
614     assert(bundle.container["general"]["module_name"] == ["Main"]);
615     assert(mutc["general"]["module_name"] == ["Two"]);
616 }
617 
618 
619 unittest
620 {
621     string[] s1 =
622         ["[general]",
623          "module_name = Main",
624          "[log]",
625          "level = info",
626          "[data_receive]",
627          "0xC000 ->		0x014B		0x0B		Рstation	yes		1		(32*{0xC000}+0)"];
628 
629     string[] s2 =
630         ["[general]",
631          "module_name = KPR",
632          "mod_type = RptR11Transceiver",
633          "[protocol]",
634          "data_flow = input"];
635 
636     auto p = immutable Parameters("[", "]", ["=", "->"], "#");
637 
638     auto bundle1 = new immutable Bundle(s1, p);
639     auto bundle2 = new immutable Bundle(s2, p);
640 
641     auto bundle = bundle1 + bundle2;
642 
643     auto value1 = bundle.value("general", "module_name");
644     assert (value1 == "Main");
645 
646     auto value2 = bundle.value("general", "mod_type");
647     assert (value2 == "RptR11Transceiver");
648 
649     auto value3 = bundle.value("log", "level");
650     assert (value3 == "info");
651 
652     auto value4 = bundle.value("protocol", "data_flow");
653     assert (value4 == "input");
654 }
655 
656 
657 unittest
658 {
659     string[] s =
660         ["[general]",
661          "module_name = Main",
662          "[log]",
663          "level = info",
664          "[data_receive]",
665          "0xC000 ->		0x014B		0x0B		Рstation	yes		1		(32*{0xC000}+0)"];
666 
667     auto p = immutable Parameters("[", "]", ["=", "->"], "#");
668 
669     auto bundle = new immutable Bundle(s, p);
670 
671     auto newBundle = bundle.subBundle("log");
672 
673     assert(newBundle.isGlKeyPresent("log") == true);
674     assert(newBundle.isGlKeyPresent("general") == false);
675     assert(newBundle.isValuePresent("log", "level") == true);
676 }
677 
678 
679 @system
680 unittest
681 {
682     import std.concurrency;
683     import core.thread;
684 
685     auto bundle = new immutable Bundle(["[general]", "module_name = KPR"]);
686 
687     static void func()
688     {
689         auto rec = receiveTimeout(dur!("msecs")(100),
690             (immutable Bundle msg)
691             {
692                 assert(msg.value("general", "module_name") == "KPR");
693             },
694         );
695     }
696 
697     auto pid = spawn(&func);
698     pid.send(bundle);
699 }
700 
701 
702 
703 private:
704 
705 
706 import std.typecons;
707 import std.string:indexOf;
708 import std.conv:to;
709 
710 
711 /++
712  * Build Bundle container from numereted array of strings
713  *
714  * Returns: builded bundle container
715  *
716  * Throws: BundleException, GlKeyNotFoundException, KeyNotFoundException, ValuesNotFoundException, Exception
717  +/
718 @trusted
719 immutable (GlValue[GlKey]) buildContainer(string[int] lines, immutable Parameters pars)
720 {
721     return parse(cleanTrash(lines, pars), pars);
722 }
723 
724 
725 @system:
726 
727 /**
728  * Parse array and place data to container
729  *
730  * Returns: builded bundle container
731  *
732  * Throws: BundleException, GlKeyNotFoundException, KeyNotFoundException, ValuesNotFoundException, Exception
733  */
734 immutable (GlValue[GlKey]) parse(string[int] lines, immutable Parameters pars)
735 {
736     import std.exception;
737 
738     GlValue[GlKey] container;
739 
740     if ((lines is null) || (lines.length == 0))
741         return assumeUnique(container);
742 
743     GlKey glKey = "";
744     Values[Key] glValue;
745     import std.algorithm.sorting:sort;
746     foreach(num; sort(lines.keys))
747     {
748         auto glKeyInLine = getFromLineGlKey(lines[num], pars);
749         if (glKeyInLine != "")
750         {
751             if((glKeyInLine in container) !is null)
752                 throw new BundleException ("Double use global key "~glKey~"  in line "~to!string(num)~": "~lines[num]);// need to testing
753             if(glKey != "") container[glKey]=glValue;
754             glKey = glKeyInLine;
755             glValue = null;
756         }
757         else
758         {
759             if (glKey=="")
760                 throw new GlKeyNotFoundException("First nonvoid line not contained global key: "~to!string(num)~": "~lines[num]);
761             Tuple!(Key, Values) parsedLine = lineToRecord(num, lines[num], pars);
762             glValue[parsedLine[0]] = parsedLine[1];
763         }
764     }
765     if(glKey!="") container[glKey]=glValue;
766 
767     GlValue[GlKey] rc = container.rehash;
768     return assumeUnique(rc);
769 }
770 
771 
772 unittest
773 {
774     string[int] lines;
775     lines[0] = "[glk1]";
776     lines[1] = "1 = a a1";
777     lines[2] = "2 = b b1";
778     lines[3] = "[glk2]";
779     lines[4] = "1 = aa aa1";
780     lines[5] = "2 = bb bb1";
781 
782     auto c = parse(lines, immutable Parameters());
783 
784     assert(c["glk1"]["1"] == ["a", "a1"]);
785     assert(c["glk1"]["2"] == ["b", "b1"]);
786 
787     assert(c["glk2"]["1"] == ["aa", "aa1"]);
788     assert(c["glk2"]["2"] == ["bb", "bb1"]);
789 }
790 
791 
792 /**
793  * Convert one string line to record
794  *
795  * Returns: packed record
796  *
797  * Throws: KeyNotFoundException, ValuesNotFoundException, Exception
798  */
799 Tuple!(Key, Values) lineToRecord(int lineNumber, string line, immutable Parameters pars)
800 {
801     import std.string:strip;
802     import std.string:split;
803 
804     ptrdiff_t separatorPos = -1;
805     string separator;
806     foreach(currentSeparator; pars.keySeparators)
807     {
808         separatorPos = line.indexOf(currentSeparator);
809         if (separatorPos >= 0)
810         {
811             separator = currentSeparator;
812             break;
813         }
814     }
815 
816     if (separatorPos <= 0)
817         throw new KeyNotFoundException ("Кеу is not present in line "~to!string(lineNumber)~": "~line);
818 
819     auto key = line[0..separatorPos].strip;
820     auto workLine = line[separatorPos+separator.length..$].strip;
821 
822     if (workLine == "") throw new ValuesNotFoundException ("Values for key is not present in line "~to!string(lineNumber)~": "~line);
823 
824     auto values = workLine.split();
825     return tuple(key, values);
826 }
827 
828 /**
829  * Seek global key in line, return it if present
830  *
831  * Returns: global key or ""
832  *
833  * Throws: Exception from string.indexOf
834  */
835 string getFromLineGlKey(string line, immutable Parameters pars)
836 {
837     auto startGlKeySymbolIndex = line.indexOf(pars.startGlKeySymbol);
838     auto endGlKeySymbolIndex = line.indexOf(pars.endGlKeySymbol);
839     if ((startGlKeySymbolIndex == 0) && (endGlKeySymbolIndex == (line.length - pars.endGlKeySymbol.length)))
840     {
841         return line[startGlKeySymbolIndex+pars.startGlKeySymbol.length..endGlKeySymbolIndex];
842     }
843     else return "";
844 }
845 
846 
847 unittest
848 {
849     auto pars = immutable Parameters();
850     string line;
851     line = "[data]";
852     assert(getFromLineGlKey(line, pars) == "data");
853 
854     auto pars1 = immutable Parameters("<<|", "|>>", ["="], "#");
855     line = "<<|tag|>>";
856     assert(getFromLineGlKey(line, pars1) == "tag");
857 }
858 
859 
860 /**
861  * read file and place data to Array of strings (Array key - line number in file)
862  *
863  * Returns: data packed in array of strings
864  *
865  * Throws: ErrnoException - open file exception
866  * Throws: StdioException - read from file exception
867  */
868 string[int] copyFileToStrings(string filePath)
869 {
870     import std.stdio;
871     import std.stdio:StdioException;
872     import std.exception:ErrnoException;
873     try
874     {
875         string[int] outStr;
876         auto rlines = File(filePath, "r").byLine();
877         int index = 0;
878         foreach(line; rlines)	outStr[++index] = to!string(line);
879         return outStr;
880     }
881     catch (ErrnoException ee)
882     {
883         throw new BundleException("errno = "~to!string(ee.errno)~" in file = "~ee.file~
884             "in line = "~to!string(ee.line)~"msg = "~ee.msg);
885     }
886     catch (StdioException ioe)
887     {
888         throw new BundleException("errno = "~to!string(ioe.errno)~" in file = "~ioe.file~
889             "in line = "~to!string(ioe.line)~"msg = "~ioe.msg);
890     }
891 }
892 
893 /**
894  * Trim in lines spaces, tabs, comments, Remove empty lines
895  *
896  * Returns: lines without trash on sides
897  *
898  * Throws: ??Exception from string.indexOf
899  */
900 string[int] cleanTrash(string[int] init, immutable Parameters pars)
901 {
902     import std.string:strip;
903 
904     if (init is null) return null;
905     else
906     {
907         string[int] _out;
908         foreach(key;init.byKey())
909         {
910             auto commentIndex = init[key].indexOf(pars.commentSymbol);
911             string s = (commentIndex>=0) ? init[key][0..commentIndex].strip : init[key].strip;
912             if (s != "") _out[key] = s;
913         }
914         return _out;
915     }
916 }
917 
918 
919 unittest
920 {
921     auto pars = immutable Parameters("[[", "]]", ["=>"], "#//");
922 
923     string[int] init;
924     init[0] = "[[data]] #// global key \"data\"";
925     init[1] = "  par1 => 58  ";
926     init[2] = "	 #//Only comment  	";
927     init[3] = "		par2 => 100		";
928 
929     auto outAr = cleanTrash(init, pars);
930     assert(init.length == 4);
931     assert(outAr.length == 3);
932     assert(outAr[0] == "[[data]]");
933     assert(outAr[1] == "par1 => 58");
934     assert(outAr[3] == "par2 => 100");
935 }
936 
937 
938 /**
939  * Convert hex string to integral number
940  *
941  * Throws: Exception
942  */
943 N strToNum(N)(string strNum) if (isIntegral!N)
944 {
945     string str;
946     uint w = 10;
947     if ((str = to!string(strNum.substitute("0x", "", "0X", ""))) != strNum)
948     {
949         w = 16;
950     }
951     else if ((str = to!string(strNum.substitute("0b", "", "0B", ""))) != strNum)
952     {
953         w = 2;
954     }
955     return to!N(str, w);
956 }
957 
958 /**
959  * Convert hex string to double
960  *
961  * Throws: Exception
962  */
963 F strToNum(F)(string strNum) if (isFloatingPoint!F)
964 {
965     if ((strNum.indexOf("0x")>=0) || (strNum.indexOf("0X")>=0) ||
966         (strNum.indexOf("0B")>=0) || (strNum.indexOf("0B")>=0))
967         return cast(double)strToNum!uint(strNum);
968     return to!F(strNum);
969 }
970 
971 /**
972  * Complement another types
973  */
974 S strToNum(S)(string strNum) nothrow pure if (isSomeString!S)
975 {
976     return strNum;
977 }
978 
979 
980 unittest
981 {
982     assert (strToNum!int("0x22") == 0x22);
983     assert (strToNum!int("0X23") == 0x23);
984     assert (strToNum!int("0b1001") == 0b1001);
985     assert (strToNum!int("0B11010") == 0b11010);
986     assert (strToNum!int("0X23") == 0x23);
987     assert (strToNum!int("-21") == -21);
988     assert (strToNum!double("0x22") == 0x22);
989     assert (strToNum!double("0X23") == 0x23);
990     assert (strToNum!double("0.25") == 0.25);
991     assert (strToNum!double("-1e-12") == -1e-12);
992 }