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 }