1 /** 2 Copyright: Copyright (c) 2015-2016 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.config.configmanager; 7 8 import std.experimental.logger; 9 import std.file : read, exists; 10 public import std.variant; 11 import std.traits : isArray; 12 import std.conv : to; 13 14 import pluginlib; 15 import sdlang; 16 17 final class ConfigOption 18 { 19 this(Variant value, Variant defaultValue) 20 { 21 this.value = value; 22 this.defaultValue = defaultValue; 23 } 24 25 T get(T)() 26 { 27 static if (isArray!T) 28 return value.get!T(); 29 else 30 return value.coerce!T(); 31 } 32 33 T set(T)(T newValue) 34 { 35 value = newValue; 36 return newValue; 37 } 38 39 Variant value; 40 Variant defaultValue; 41 } 42 43 final class ConfigManager : IResourceManager 44 { 45 private: 46 ConfigOption[string] options; 47 string filename; 48 49 public: 50 51 override string id() @property { return "voxelman.managers.configmanager"; } 52 53 this(string filename) 54 { 55 this.filename = filename; 56 } 57 58 override void loadResources() 59 { 60 load(); 61 } 62 63 // Runtime options are not saved. Use them to store global options that need no saving 64 ConfigOption registerOption(T)(string optionName, T defaultValue) 65 { 66 if (auto opt = optionName in options) 67 return *opt; 68 auto option = new ConfigOption(Variant(defaultValue), Variant(defaultValue)); 69 options[optionName] = option; 70 return option; 71 } 72 73 ConfigOption opIndex(string optionName) 74 { 75 return options.get(optionName, null); 76 } 77 78 void load() 79 { 80 if (!exists(filename)) 81 return; 82 83 Tag root; 84 85 try 86 { 87 string fileData = cast(string)read(filename); 88 root = parseSource(fileData, filename); 89 } 90 catch(SDLangParseException e) 91 { 92 warning(e.msg); 93 return; 94 } 95 96 foreach(optionPair; options.byKeyValue) 97 { 98 if (optionPair.key !in root.tags) continue; 99 auto tags = root.tags[optionPair.key]; 100 101 if (tags.length == 1) 102 { 103 try 104 { 105 parseValue(optionPair.value, optionPair.key, tags[0].values); 106 //infof("%s %s %s", optionPair.key, optionPair.value.value, optionPair.value.defaultValue); 107 } 108 catch(VariantException e) 109 { 110 warningf("Error parsing config option: %s - %s", optionPair.key, e.msg); 111 } 112 } 113 else if (tags.length > 1) 114 warningf("Multiple definitions of '%s'", optionPair.key); 115 else 116 warningf("Empty option '%s'", optionPair.key); 117 } 118 } 119 120 void save() {} 121 122 private: 123 124 static void parseValue(ConfigOption option, string optionName, Value[] values) 125 { 126 if (values.length == 1) 127 { 128 Value value = values[0]; 129 130 if (option.value.type == typeid(bool)) { 131 option.value = Variant(value.coerce!bool); 132 } 133 else if (option.value.type == typeid(string)) { 134 option.value = Variant(value.coerce!string); 135 } 136 else if (option.value.convertsTo!long) { 137 option.value = Variant(value.coerce!long); 138 } 139 else if (option.value.convertsTo!real) { 140 option.value = Variant(value.coerce!double); 141 } 142 else 143 { 144 warningf("Cannot parse '%s' from '%s'", optionName, value.to!string); 145 } 146 } 147 else if (values.length > 1) 148 { 149 void parseArray(T)() 150 { 151 T[] items; 152 foreach(v; values) 153 items ~= v.coerce!T; 154 option.value = Variant(items); 155 } 156 157 info(option.value.convertsTo!(long[])); 158 159 if (option.value.type == typeid(long[])) { 160 if (option.value.length != values.length) 161 return; 162 163 parseArray!long; 164 } 165 else if (option.value.type == typeid(int[])) { 166 if (option.value.length != values.length) 167 return; 168 169 parseArray!int; 170 } 171 if (option.value.type == typeid(ulong[])) { 172 if (option.value.length != values.length) 173 return; 174 175 parseArray!ulong; 176 } 177 else if (option.value.type == typeid(uint[])) { 178 if (option.value.length != values.length) 179 return; 180 181 parseArray!uint; 182 } 183 else if (option.value.type == typeid(real[]) || 184 option.value.type == typeid(double[]) || 185 option.value.type == typeid(float[])) { 186 if (option.value.length != values.length) 187 return; 188 189 parseArray!double; 190 } 191 else 192 { 193 warningf("Cannot parse '%s' from '%s'", optionName, values.to!string); 194 } 195 } 196 } 197 }