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