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 Variant value; 34 Variant defaultValue; 35 } 36 37 final class ConfigManager : IResourceManager 38 { 39 private: 40 ConfigOption[string] options; 41 string filename; 42 43 public: 44 45 override string id() @property { return "voxelman.managers.configmanager"; } 46 47 this(string filename) 48 { 49 this.filename = filename; 50 } 51 52 override void loadResources() 53 { 54 load(); 55 } 56 57 // Runtime options are not saved. Use them to store global options that need no saving 58 ConfigOption registerOption(T)(string optionName, T defaultValue) 59 { 60 auto option = new ConfigOption(Variant(defaultValue), Variant(defaultValue)); 61 options[optionName] = option; 62 return option; 63 } 64 65 ConfigOption opIndex(string optionName) 66 { 67 return options.get(optionName, null); 68 } 69 70 void load() 71 { 72 if (!exists(filename)) 73 return; 74 75 Tag root; 76 77 try 78 { 79 string fileData = cast(string)read(filename); 80 root = parseSource(fileData, filename); 81 } 82 catch(SDLangParseException e) 83 { 84 warning(e.msg); 85 return; 86 } 87 88 foreach(optionPair; options.byKeyValue) 89 { 90 auto tags = root.tags[optionPair.key]; 91 92 if (tags.length == 1) 93 { 94 try 95 { 96 parseValue(optionPair.value, optionPair.key, tags[0].values); 97 //infof("%s %s %s", optionPair.key, optionPair.value.value, optionPair.value.defaultValue); 98 } 99 catch(VariantException e) 100 { 101 warningf("Error parsing config option: %s - %s", optionPair.key, e.msg); 102 } 103 } 104 else if (tags.length > 1) 105 warningf("Multiple definitions of '%s'", optionPair.key); 106 else 107 warningf("Empty option '%s'", optionPair.key); 108 } 109 } 110 111 void save() {} 112 113 private: 114 115 static void parseValue(ConfigOption option, string optionName, Value[] values) 116 { 117 if (values.length == 1) 118 { 119 Value value = values[0]; 120 121 if (option.value.type == typeid(bool)) { 122 option.value = Variant(value.coerce!bool); 123 } 124 else if (option.value.type == typeid(string)) { 125 option.value = Variant(value.coerce!string); 126 } 127 else if (option.value.convertsTo!long) { 128 option.value = Variant(value.coerce!long); 129 } 130 else if (option.value.convertsTo!real) { 131 option.value = Variant(value.coerce!double); 132 } 133 else 134 { 135 warningf("Cannot parse '%s' from '%s'", optionName, value.to!string); 136 } 137 } 138 else if (values.length > 1) 139 { 140 void parseArray(T)() 141 { 142 T[] items; 143 foreach(v; values) 144 items ~= v.coerce!T; 145 option.value = Variant(items); 146 } 147 148 info(option.value.convertsTo!(long[])); 149 150 if (option.value.type == typeid(long[])) { 151 if (option.value.length != values.length) 152 return; 153 154 parseArray!long; 155 } 156 else if (option.value.type == typeid(int[])) { 157 if (option.value.length != values.length) 158 return; 159 160 parseArray!int; 161 } 162 if (option.value.type == typeid(ulong[])) { 163 if (option.value.length != values.length) 164 return; 165 166 parseArray!ulong; 167 } 168 else if (option.value.type == typeid(uint[])) { 169 if (option.value.length != values.length) 170 return; 171 172 parseArray!uint; 173 } 174 else if (option.value.type == typeid(real[]) || 175 option.value.type == typeid(double[]) || 176 option.value.type == typeid(float[])) { 177 if (option.value.length != values.length) 178 return; 179 180 parseArray!double; 181 } 182 else 183 { 184 warningf("Cannot parse '%s' from '%s'", optionName, values.to!string); 185 } 186 } 187 } 188 }