1 
2 
3 module mars.defs;
4 
5 import std.meta;
6 import std.traits;
7 import std.algorithm;
8 import std.array;
9 import std.meta;
10 import std.typecons;
11 
12 import mars.starwars;
13 
14 /+
15 struct Schema2 
16 {
17     this(string name, string[] tt) {
18         this.name = name;
19         foreach(t; tt){
20             this.tables ~= Table2(&this, t);
21         }
22     }
23 
24     string name;
25     Table2[] tables;
26 }
27 struct Table2 {
28     Schema2* schema;
29     string name;
30 }
31 enum s1 = Schema2("s1", ["t1", "t2"]);
32 static assert(s1.tables.length == 2);
33 static assert(s1.tables[0].schema is s1);
34 
35 auto schema(string name){ return Schema2(name, []); }
36 auto table(Schema2 schema, string name){
37     string[] tt; foreach(t; schema.tables){ tt ~= t.name; }
38     return Schema2(schema.name, schema.tables.map!"a.name"().array ~ name);
39 }
40 
41 enum ss = schema("foo");
42 enum s2 = schema("foo").table("bar");
43 +/
44 
45 struct Schema {
46     string name;
47     immutable(Table)[] tables;
48 
49     immutable(Table) tableNamed(string name) const @safe {
50         return tables.find!((t) => t.name == name)[0];
51     }
52 }
53 /+
54 Schema table(immutable(Schema) schema, string name, immutable(Col)[] columns, immutable(size_t)[] primaryKey, immutable(Reference)[] references, size_t index){
55     auto s = Schema(schema.name, schema.tables ~ immutable(Table)(name, columns, primaryKey, references, index));
56     return s;
57 }
58 unittest {
59     enum s = Schema("foo").table("bar", [immutable(Col)("c1", Type.integer)], [], [], 0);
60     static assert( s.tables.length == 1 );
61     enum s2 = Schema("foo")
62         .table("bar", [immutable(Col)("c1", Type.integer)], [], [], 0)
63         .table("bar", [immutable(Col)("c1", Type.integer)], [], [], 1)
64         ;
65     static assert( s.tables.length == 2 );
66 }
67 +/
68 
69 struct Sync {
70     string mars_who, mars_what, mars_when;
71 }
72 
73 enum SyncCols = [
74     immutable(Col)("mars_who", Type.text, false), 
75     immutable(Col)("mars_what", Type.text, false), 
76     immutable(Col)("mars_when", Type.text, false),
77 ];
78 
79 struct Table { 
80     string name;
81     immutable(Col)[] columns;
82     size_t[] primaryKey;
83     Reference[] references;
84     size_t index; /// the unique index of the table in the system.
85     bool durable = true;
86     bool decorateRows = false;
87     bool cacheRows = true;
88 
89     immutable(Schema)* schema;
90 
91     @property immutable(Col)[] decoratedCols() const { return columns ~ SyncCols; }
92 
93     /*
94     If the table primary key is set by the terver, we need to return it to the client. */
95     const(Col)[] returningCols() const
96     {
97         import std.algorithm : canFind, filter;
98         import std.array : array;
99         import std.range : indexed;
100         
101         return pkCols
102             .filter!( (c) => [Type.smallserial, Type.serial].canFind(c.type) )
103             .array;
104     }
105     
106     /**
107      * returns the primary keys columns of a table */
108     immutable(Col)[] pkCols() const
109     {
110         import std.array : array;
111         import std.range : indexed;
112 
113         return columns.indexed(primaryKey).array;
114     }
115     unittest {
116         static assert(Landings.pkCols.length == Landings.primaryKey.length);
117     }
118 
119 }
120 unittest
121 {
122     immutable static t1 = immutable(Table)("t", [
123             Col("c1", Type.integer), Col("c2", Type.text), Col("c3", Type.integer)
124     ], [0,2], []);
125 
126     static assert( t1.pkCols() == [t1.columns[0], t1.columns[2]] );
127     
128     enum columns = t1.columns;
129     void f1(asD!columns cols){ int i = cols[0]; string s = cols[1]; int j = cols[2]; } 
130     f1(1, "foo", 2);
131 }
132 
133 struct Col {
134     string name;
135     Type type;
136     bool null_;
137 }
138 
139 struct Reference {
140     size_t[] referenceCols;
141     string referencedTable;
142     size_t[] referencedCols;
143 }
144 
145 enum Type { 
146     unknown, 
147     boolean,
148     date, 
149     real_, doublePrecision, 
150     smallint, integer, bigint, // 16, 32, 64 bit, postgresql
151     smallserial, serial, 
152     text, varchar,
153     bytea 
154 }
155 
156 /**
157  * returns the D type for the passed SQL type. */
158 template asD(alias t) if( is(Unqual!(typeof(t)) == Type) )
159 {
160 
161     static if( t == Type.integer )              alias asD = int;
162     else static if( t == Type.bigint )          alias asD = long;
163     else static if( t == Type.text )            alias asD = string;
164     else static if( t == Type.serial )          alias asD = int;
165     else static if( t == Type.boolean )         alias asD = bool;
166     else static if( t == Type.smallint )        alias asD = short;
167     else static if( t == Type.smallserial )     alias asD = short;
168     else static if( t == Type.real_ )           alias asD = float;
169     else static if( t == Type.doublePrecision ) alias asD = double;
170     else static if( t == Type.date )            alias asD = Date;
171     else static if( t == Type.bytea )           alias asD = ubyte[];
172     else static assert(false);
173 }
174 
175 /**
176  * returns the D type for a column. */
177 template asD(alias c) if( is(Unqual!(typeof(c)) == Col) )
178 {
179     enum t = c.type;
180     alias asD = .asD!(t);
181 }
182 static assert( is(asD!( Col("c", Type.integer) ) == int) );
183 static assert( is(asD!( Col("c", Type.real_) ) == float ) );
184 
185 /**
186  * returns the D type for a sequence of columns. */
187 template asD(alias c) if( is(Unqual!(typeof(c)) : immutable(Col)[]) || is(Unqual!(typeof(c)) : Col[]) )
188 {
189 
190     enum cols = c;
191     static if(cols.length == 1){
192         alias asD = AliasSeq!(asD!(cols[0]));
193     }
194     else static if(cols.length >1){
195         alias asD = AliasSeq!(asD!(cols[0]), asD!(cols[1 .. $]));
196     }
197     else static assert(false);
198 }
199 static assert( is(asD!( [Col("c", Type.integer), Col("d", Type.text)] ) == AliasSeq!(int, string)) );
200 
201 /** 
202  * returns the D type and the name of the column. */
203 private template asStruct_(alias c, string p ="") if( is(Unqual!(typeof(c)) : immutable(Col)[]) || is(Unqual!(typeof(c)) : Col[]) )
204 {
205 
206     enum cols = c;
207     enum prefix = p;
208     static if(cols.length == 1){
209         alias t = asD!(cols[0]);
210         enum string n = cols[0].name;
211         enum string asStruct_ = t.stringof ~ " " ~ prefix ~ n ~ ";"; //AliasSeq!(asD!(cols[0])););
212     }
213     else static if(cols.length >1){
214         enum string asStruct_ = (asD!(cols[0])).stringof ~ " " ~ prefix ~ cols[0].name ~ "; " ~ asStruct_!(cols[1 .. $], prefix);
215     }
216     else static assert(false, cols.length);
217 }
218 
219 
220 
221 template asStruct(alias t)
222 {
223     enum cols = t.columns;
224     enum string structName = t.name ~ "Row";
225     enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols) ~ "}";
226     mixin(def ~"; alias asStruct = " ~ structName ~ ";");
227 }
228 static assert(is( asStruct!(Table("t", [immutable(Col)("c1", Type.integer), immutable(Col)("c2", Type.text)], [], [])) == struct )); 
229 
230 /// I can't use here an 'alias this' composition, as it would be serialised wrongly.
231 template asSyncStruct(alias t)
232 {
233     enum cols = t.decoratedCols;
234     enum string structName = t.name ~ "SyncRow";
235     enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols) ~ "}";
236     mixin(def ~"; alias asSyncStruct = " ~ structName ~ ";");
237 }
238 
239 template asPkStruct(alias t)
240 {
241     static if( t.pkCols.length >0 ){
242         enum cols = t.pkCols;
243     }
244     else {
245         enum cols = t.columns;
246     }
247     enum string structName = t.name ~ "PkRow";
248     enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols) ~ "}";
249     mixin(def ~"; alias asPkStruct = " ~ structName ~ ";");
250 }
251 unittest {
252     static assert(is(asPkStruct!(Table("t", [immutable(Col)("c1", Type.integer), immutable(Col)("c2", Type.text)], [0], [])) == struct ));
253     static assert( FieldNameTuple!(asPkStruct!Landings) == AliasSeq!("person_name", "planet_name"));
254 }
255 
256 template asPkParamStruct(alias t)
257 {
258     static if( t.pkCols.length >0 ){
259         enum cols = t.pkCols;
260     }
261     else {
262         enum cols = t.columns;
263     }
264     enum string structName = t.name ~ "PkRow";
265     enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols, "key") ~ "}";
266     mixin(def ~"; alias asPkParamStruct = " ~ structName ~ ";");
267 }
268 unittest {
269     static assert(is(asPkStruct!(Table("t", [immutable(Col)("c1", Type.integer), immutable(Col)("c2", Type.text)], [0], [])) == struct ));
270     static assert( FieldNameTuple!(asPkParamStruct!Landings) == AliasSeq!("keyperson_name", "keyplanet_name"));
271 
272 }
273 
274 template asSyncPkParamStruct(alias t)
275 {
276     static if( t.pkCols.length >0 ){
277         enum cols = t.pkCols ~ SyncCols;
278     }
279     else {
280         enum cols = t.decoratedCols;
281     }
282     enum string structName = t.name ~ "SyncPkParamRow";
283     enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols, "key") ~ "}";
284     mixin(def ~"; alias asSyncPkParamStruct = " ~ structName ~ ";");
285 }
286 static assert(is(asPkParamStruct!(Table("t", [immutable(Col)("c1", Type.integer), immutable(Col)("c2", Type.text)], [0], [])) == struct ));
287 
288 
289 
290 /**
291  * returns the values of the primary keys of this table row. */
292 auto pkValues(alias table)(const asStruct!table fixture)
293 {
294     asPkStruct!table keys;
295     assignCommonFields(keys, fixture);
296     return keys;
297 }
298 unittest {
299     static assert(luke.pkValues!People.name == "Luke");
300 }
301 
302 /**
303  * returns the values of the primary keys of this table row. */
304 auto pkParamValues(alias table)(const asStruct!table fixture)
305 {
306     asPkStruct!table keys;
307     asPkParamStruct!table paramKeys;
308     assignCommonFields(keys, fixture);
309     assignFields(paramKeys, keys);
310     return paramKeys;
311 }
312 unittest {
313     static assert(luke.pkParamValues!People.keyname == "Luke");
314 }
315 
316 void assignCommonFields(R, V, size_t i = 0)(ref R r, V v)
317 {
318     alias NamesR = FieldNameTuple!R;
319     alias NamesV = FieldNameTuple!V;
320 
321     enum string Name = NamesV[i];
322 
323 
324     enum j = staticIndexOf!(Name, NamesR);
325     static if( j != -1 ){
326         r.tupleof[j] = v.tupleof[i];
327     }
328     static if(i+1 < NamesV.length){
329         assignCommonFields!(R, V, i+1)(r, v);
330     }
331 }
332 unittest 
333 {
334     struct V { string a; string b; string c; }
335     struct R { string b; string a; }
336     struct X { int b; string a; }
337 
338     V v = V("a", "b", "c");
339     R r;
340     X x;
341 
342     static assert(!__traits(compiles, assignCommonFields!(X, V)(x, v) ));
343 
344     assignCommonFields!(R, V)(r, v);
345     assert( r == R("b", "a")) ;
346 
347 }
348 
349 void assignFields(R, V, size_t i = 0)(ref R r, V v)
350 {
351     r.tupleof[i] = v.tupleof[i];
352     static if(i+1 < v.tupleof.length) assignFields!(R, V, i+1)(r, v);
353 }
354 unittest
355 {
356     struct V { string a; int b;}
357     struct R { string c; int d;}
358     auto v = V("hello", 42);
359     R r;
360     assignFields(r, v);
361     assert( r == R("hello", 42) );
362 }