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 }