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 }