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 }