1 module gfx.decl.store; 2 3 import gfx.core.rc : AtomicRefCounted, Disposable; 4 import std.variant : Algebraic; 5 6 class NoSuchKeyException : Exception 7 { 8 string key; 9 10 private this(in string key, in Store store) 11 { 12 import std.format : format; 13 this.key = key; 14 super(format("no such key in %s store: %s", store, key)); 15 } 16 } 17 18 class WrongTypeException : Exception 19 { 20 string type; 21 string key; 22 23 private this(in string type, in string key, in Store store) 24 { 25 import std.format : format; 26 this.type = type; 27 this.key = key; 28 super(format("key \"%s\" in store %s did not find the expected type: %s", key, store, type)); 29 } 30 } 31 32 class DeclarativeStore : Disposable 33 { 34 private AtomicRefCounted[string] _rcStore; 35 private Variant[string] _valueStore; 36 private Object[string] _objStore; // any class, including ObjectValue!T 37 38 T store(T)(in string key, T value) { 39 enum store = storeOf!T; 40 static if (store == Store.rc) { 41 value.retain(); 42 auto former = key in _rcStore; 43 if (former) { 44 former.release(); 45 *former = value; 46 } 47 else { 48 _rcStore[key] = value; 49 } 50 } 51 else static if (store == Store.value) { 52 _valueStore[key] = value; 53 } 54 else static if (store == Store.obj && is(T : Object)) { 55 static assert(!is(T : Disposable)); 56 assert(!cast(Disposable)value); 57 _objStore[key] = value; 58 } 59 else static if (store == Store.obj && !is(T : Object)) { 60 _objStore[key] = new ValueObject!T(value); 61 } 62 else static assert(false); 63 return value; 64 } 65 66 T expect(T)(in string key) 67 { 68 enum store = storeOf!T; 69 T val = void; 70 if (tryLookUp!(T, store)(key, val)) { 71 return val; 72 } 73 else { 74 throw new NoSuchKeyException(key, store); 75 } 76 } 77 78 T get(T)(in string key, T def=T.init) 79 { 80 enum store = storeOf!T; 81 T val = void; 82 if (tryLookUp!(T, store)(key, val)) { 83 return val; 84 } 85 else { 86 return def; 87 } 88 } 89 90 private bool tryLookUp(T, Store store)(in string key, out T val) 91 { 92 static if (store == Store.rc) { 93 auto pval = key in _rcStore; 94 if (pval) { 95 val = cast(T)(*pval); 96 if (val) return true; 97 else throw new WrongTypeException(T.stringof, key, store); 98 } 99 } 100 else static if (store == Store.value) { 101 auto pval = key in _valueStore; 102 if (pval) { 103 if (pval.convertsTo!T) { 104 val = pval.get!T(); 105 return true; 106 } 107 else throw new WrongTypeException(T.stringof, key, store); 108 } 109 } 110 else static if (store == Store.obj) { 111 auto pval = key in _objStore; 112 if (pval) { 113 static if (is(T : Object)) { 114 val = cast(T)(*pval); 115 if (val) return true; 116 } 117 else { 118 auto vo = cast(ValueObject!T)(*pval); 119 if (vo) { 120 val = vo.payload; 121 return true; 122 } 123 } 124 throw new WrongTypeException(T.stringof, key, store); 125 } 126 } 127 else static assert(false); 128 return false; 129 } 130 131 bool remove(in string key) 132 { 133 bool[3] found; 134 135 auto rc = key in _rcStore; 136 if (rc) { 137 rc.release(); 138 _rcStore.remove(key); 139 found[0] = true; 140 } 141 142 found[1] = _valueStore.remove(key); 143 found[2] = _objStore.remove(key); 144 145 return found[0] || found[1] || found[2]; 146 } 147 148 override void dispose() { 149 import gfx.core.rc : releaseArray; 150 releaseArray(_rcStore); 151 _valueStore.clear(); 152 _objStore.clear(); 153 } 154 155 } 156 157 private: 158 159 alias Variant = Algebraic!(int, uint, long, ulong, float, double); 160 161 enum fitsVariant(T) = Variant.allowed!T; 162 163 enum Store { 164 rc, value, obj, 165 } 166 167 template storeOf(T) 168 { 169 static if (is(T : AtomicRefCounted)) { 170 enum storeOf = Store.rc; 171 } 172 else static if (fitsVariant!T) { 173 enum storeOf = Store.value; 174 } 175 else { 176 enum storeOf = Store.obj; // either class, interface or ObjectValue 177 } 178 } 179 180 enum needValueObject(T) = (storeOf!T == Store.obj) && !is(T : Object); 181 182 class ValueObject(T) { 183 T payload; 184 this(T payload) { 185 this.payload = payload; 186 } 187 }