1 module variantconfig; 2 3 import std.string : lineSplitter, split, strip, indexOf; 4 import std.stdio : File, writeln; 5 import std.file : exists, readText; 6 import std.algorithm : sort, find; 7 import std.traits : isNumeric; 8 import std.path : isValidFilename; 9 10 public import std.variant; 11 12 struct VariantConfig 13 { 14 private: 15 void load(immutable string fileName) @safe 16 { 17 string text; 18 19 if(fileName.indexOf("=") == -1) 20 { 21 if(exists(fileName)) 22 { 23 text = readText(fileName); 24 } 25 } 26 else 27 { 28 text = fileName; // In this case it's a string not a filename. 29 } 30 31 processText(text); 32 } 33 34 void save() @trusted 35 { 36 auto configfile = File(fileName_, "w+"); 37 38 foreach(key; sort(values_.keys)) 39 { 40 configfile.writeln(key, separator_, values_[key]); 41 } 42 } 43 44 void processText(immutable string text) @safe 45 { 46 auto lines = text.lineSplitter(); 47 48 foreach(line; lines) 49 { 50 auto fields = split(line, separator_); 51 52 if(fields.length == 2) 53 { 54 values_[fields[0].strip] = fields[1].strip; 55 } 56 } 57 } 58 public: 59 this(immutable string fileName) @safe 60 { 61 fileName_ = fileName; 62 load(fileName); 63 } 64 65 ~this() @safe 66 { 67 if(valuesModified_) 68 { 69 save(); 70 } 71 } 72 73 bool hasValue(immutable string key) pure @safe 74 { 75 if(key in values_) 76 { 77 return true; 78 } 79 return false; 80 } 81 82 Variant getValue(immutable string key) @safe 83 { 84 return values_.get(key, Variant(0)); 85 } 86 87 Variant getValue(immutable string key, Variant defval) @safe 88 { 89 return values_.get(key, Variant(defval)); 90 } 91 92 void setValue(immutable string key, Variant value) @safe 93 { 94 values_[key] = value; 95 valuesModified_ = true; 96 } 97 98 bool remove(immutable string key) pure @safe 99 { 100 return values_.remove(key); 101 } 102 103 Variant opIndex(immutable string key) @safe 104 { 105 return getValue(key); 106 } 107 108 void opIndexAssign(T)(T value, immutable string key) @safe 109 { 110 setValue(key, Variant(value)); 111 } 112 113 private: 114 immutable char separator_ = '='; 115 Variant[string] values_; 116 immutable string fileName_; // Only used for saving 117 bool valuesModified_; 118 } 119 120 int toInt(Variant value) @safe 121 { 122 return value.coerce!(int); 123 } 124 125 long toLong(Variant value) @safe 126 { 127 return value.coerce!(long); 128 } 129 130 bool toBool(Variant value) @safe 131 { 132 return value.coerce!(bool); 133 } 134 135 double toDouble(Variant value) @safe 136 { 137 return value.coerce!(double); 138 } 139 140 real toReal(Variant value) @safe 141 { 142 return value.coerce!(real); 143 } 144 145 string toStr(Variant value) @safe // NOTE: Must be named toStr instead of toString or D buildin will override. 146 { 147 if(value == 0) 148 { 149 return ""; 150 } 151 return value.coerce!(string); 152 } 153 154 unittest 155 { 156 string test = " 157 aBool=true 158 float=3443.443 159 number=12071 160 sentence=This is a really long sentence to test for a really long value string! 161 time=12:04 162 "; 163 164 auto config = VariantConfig(test); 165 long number = config["number"].toLong; 166 bool aBool = config["aBool"].toBool; 167 string sentence = config["sentence"].toStr; 168 string time = config["time"].toStr; 169 double aDouble = config["aDouble"].toDouble; 170 171 assert(number == 12071); 172 assert(aBool == true); 173 assert(sentence == "This is a really long sentence to test for a really long value string!"); 174 assert(time == "12:04"); 175 assert(aDouble == 3443.443); 176 }