1 module mysql.row;
2 
3 
4 import std.algorithm;
5 import std.datetime;
6 import std.traits;
7 import std.typecons;
8 
9 import mysql.exception;
10 import mysql.type;
11 
12 
13 template isWritableDataMember(T, string Member) {
14 	static if (is(TypeTuple!(__traits(getMember, T, Member)))) {
15         enum isWritableDataMember = false;
16     } else static if (!is(typeof(__traits(getMember, T, Member)))) {
17 		enum isWritableDataMember = false;
18     } else static if (is(typeof(__traits(getMember, T, Member)) == void)) {
19         enum isWritableDataMember = false;
20     } else static if (isSomeFunction!(typeof(__traits(getMember, T, Member)))) {
21         enum isWritableDataMember = false;
22     } else static if (!is(typeof((){ T x = void; __traits(getMember, x, Member) = __traits(getMember, x, Member); }()))) {
23         enum isWritableDataMember = false;
24     } else static if ((__traits(getProtection, __traits(getMember, T, Member)) != "public") && (__traits(getProtection, __traits(getMember, T, Member)) != "export")) {
25         enum isWritableDataMember = false;
26     } else {
27         enum isWritableDataMember = true;
28     }
29 }
30 
31 
32 enum Strict {
33     yes = 0,
34     no,
35 }
36 
37 
38 struct MySQLRow {
39     package void header(MySQLHeader header) {
40         index_ = null;
41         names_.length = header.length;
42         foreach (index, column; header) {
43             names_[index] = column.name;
44             index_[column.name] = index;
45         }
46     }
47 
48     package void set(size_t index, MySQLValue x) {
49         values_[index] = x;
50     }
51 
52     package void nullify(size_t index) {
53         values_[index].nullify();
54     }
55 
56     package @property length(size_t x) {
57         values_.length = x;
58     }
59 
60     @property length() const {
61         return values_.length;
62     }
63 
64     @property const(string)[] columns() const {
65         return names_;
66     }
67 
68     @property MySQLValue opDispatch(string key)() const {
69         return opIndex(key);
70     }
71 
72     MySQLValue opIndex(string key) const {
73         if (auto pindex = key in index_)
74             return values_[*pindex];
75         throw new MySQLErrorException("Column '" ~ key ~ "' was not found in this result set");
76     }
77 
78     MySQLValue opIndex(size_t index) const {
79         return values_[index];
80     }
81 
82     const(MySQLValue)* opBinaryRight(string op)(string key) const if (op == "in") {
83         if (auto pindex = key in index_)
84             return &values_[*pindex];
85         return null;
86     }
87 
88 	int opApply(int delegate(const ref MySQLValue value) del) const {
89         foreach (ref v; values_)
90             if (auto ret = del(v))
91                 return ret;
92         return 0;
93     }
94 
95 	int opApply(int delegate(ref size_t, const ref MySQLValue) del) const {
96         foreach (ref size_t i, ref v; values_)
97             if (auto ret = del(i, v))
98                 return ret;
99         return 0;
100     }
101 
102 	int opApply(int delegate(const ref string, const ref MySQLValue) del) const {
103         foreach (size_t i, ref v; values_)
104             if (auto ret = del(names_[i], v))
105                 return ret;
106         return 0;
107     }
108 
109     string toString() const {
110         import std.conv;
111         return to!string(values_);
112     }
113 
114     string[] toStringArray(size_t start = 0, size_t end = ~cast(size_t)0) const {
115         end = min(end, values_.length);
116         start = min(start, values_.length);
117         if (start > end)
118             swap(start, end);
119 
120         string[] result;
121         result.reserve(end - start);
122         foreach(i; start..end)
123             result ~= values_[i].toString;
124         return result;
125     }
126 
127     void toStruct(T, Strict strict = Strict.yes)(ref T x) if(is(Unqual!T == struct)) {
128         static if (isTuple!(Unqual!T)) {
129             foreach(i, ref f; x.field) {
130                 static if (strict == Strict.yes) {
131                     f = this[i].get!(Unqual!(typeof(f)));
132                 } else {
133                     if (!this[i].isNull)
134                         f = this[i].get!(Unqual!(typeof(f)));
135                 }
136             }
137         } else {
138             structurize!(T, strict, null)(x);
139         }
140     }
141 
142     T toStruct(T, Strict strict = Strict.yes)() if (is(Unqual!T == struct)) {
143         T result;
144         toStruct!(T, strict)(result);
145         return result;
146     }
147 
148 private:
149     void structurize(T, Strict strict = Strict.yes, string path = null)(ref T result) {
150         foreach(member; __traits(allMembers, T)) {
151             static if (isWritableDataMember!(T, member)) {
152                 enum pathMember = path ~ member;
153                 alias MemberType = typeof(__traits(getMember, result, member));
154 
155                 static if (is(Unqual!MemberType == struct) && !is(Unqual!MemberType == Date) && !is(Unqual!MemberType == DateTime) && !is(Unqual!MemberType == SysTime) && !is(Unqual!MemberType == Duration)) {
156                     enum pathNew = pathMember ~ ".";
157                     structurize!(MemberType, strict, pathNew)(__traits(getMember, result, member));
158                 } else {
159                     static if (strict == Strict.yes) {
160                         __traits(getMember, result, member) = this[pathMember].get!(Unqual!MemberType);
161                     } else {
162                         auto pvalue = pathMember in this;
163                         if (pvalue && !pvalue.isNull)
164                             __traits(getMember, result, member) = pvalue.get!(Unqual!MemberType);
165                     }
166                 }
167             }
168         }
169     }
170 
171     MySQLValue[] values_;
172     string[] names_;
173     size_t[string] index_;
174 }