1 module dpq.meta;
2 
3 import dpq.attributes;
4 
5 import std.traits;
6 import std.typecons : Nullable;
7 import std.datetime : SysTime;
8 
9 version(unittest) import std.stdio;
10 
11 deprecated("Use SQLType instead, not all array types are supported by sqlType")
12 string sqlType(T)()
13 {
14 	// TODO: More types, embedded structs, Date types
15 
16 	static if (isArray!T)
17 		alias FT = ForeachType!T;
18 	else
19 		alias FT = T;
20 
21 	static if (is(FT == int) || is(FT == ushort))
22 		enum type = "INT";
23 	else static if (is(FT == long) || is(FT == uint))
24 		enum type = "INT8";
25 	else static if (is(FT == short))
26 		enum type = "INT2";
27 	else static if (is(FT == long))
28 		enum type = "BIGINT";
29 	else static if (is(FT == float))
30 		enum type = "FLOAT4";
31 	else static if (is(FT == double))
32 		enum type = "FLOAT8";
33 	else static if (is(FT == char[]) || is(FT == string))
34 		enum type = "TEXT";
35 	else static if (is(FT == bool))
36 		enum type = "BOOL";
37 	else static if (is(FT == char))
38 		enum type = "CHAR(1)";
39 	else static if(is(FT == ubyte[]) || is(FT == byte[]))
40 		enum type = "BYTEA";
41 	else static if (is(FT == enum))
42 		enum type = sqlType!(OriginalType!FT);
43 	else
44 		static assert(false, "Cannot map type \"" ~ T.stringof ~ "\" to any PG type, please specify it manually using @type.");
45 
46 	static if (isArray!T)
47 	{
48 		static if (isStaticArray!T)
49 			return type ~ "[%d]".format(T.length);
50 		else
51 			return type ~ "[]";
52 	}
53 	else
54 		return type;
55 }
56 
57 /**
58 	Returns the array's base type
59 
60 	Returns the string type for any type that returns true
61 	for isSomeString!T
62 
63 	Examples:
64 	---------------
65 	alias T = BaseType!(int[][]);
66 	alias T2 = BaseType!(int[]);
67 	alias T3 = BaseType!int;
68 	alias T4 = BaseType!(string[])
69 
70 	static assert(is(T == int));
71 	static assert(is(T2 == int));
72 	static assert(is(T3 == int));
73 	static assert(is(T4 == string));
74 	---------------
75 */
76 template BaseType(T)
77 {
78 	import std.typecons : TypedefType;
79 	static if (isArray!T && !isSomeString!T)
80 		alias BaseType = BaseType!(ForeachType!T);
81 	else
82 		alias BaseType = TypedefType!(Unqual!T);
83 }
84 
85 unittest
86 {
87 	writeln(" * meta");
88 	writeln("\t * BaseType");
89 
90 	static assert(is(BaseType!(int[][][]) == int));
91 	static assert(is(BaseType!(string[]) == string));
92 	static assert(is(BaseType!string == string));
93 	static assert(is(BaseType!dstring == dstring));
94 }
95 
96 template SQLType(T)
97 {
98 	alias BT = BaseType!T;
99 
100 	static if(isInstanceOf!(Nullable, T))
101 	{
102 		enum SQLType = SQLType!(Unqual!(typeof(T.get)));
103 		//enum isNullable = true;
104 	}
105 	else
106 	{
107 		enum isNullable = false;
108 		static if(is(T == ubyte[]) || is(T == byte[]))
109 			enum SQLType = "BYTEA";
110 		else
111 		{
112 			static if (isSomeString!BT)
113 				enum type = "TEXT";
114 			else static if (is(BT == SysTime))
115 				enum type = "timestamp";
116 			else static if (is(BT == int))
117 				enum type = "INT4";
118 			else static if (is(BT == long))
119 				enum type = "INT8";
120 			else static if (is(BT == short))
121 				enum type = "INT2";
122 			else static if (is(BT == float))
123 				enum type = "FLOAT4";
124 			else static if (is(BT == double))
125 				enum type = "FLOAT8";
126 			else static if (is(BT == bool))
127 				enum type = "BOOL";
128 			else static if (is(BT == char))
129 				enum type = "CHAR(1)";
130 			else static if (is(BT == enum))
131 				enum type = SQLType!(OriginalType!BT);
132 			else
133 				static assert(false,
134 						"Cannot map type \"" ~ T.stringof ~ "\" to any PG type, " ~
135 						"please note that embedded structures need an @embed " ~
136 						"or @type attribute if you do not wish to embed them.");
137 
138 			static if (isArray!T && !isSomeString!T)
139 				enum SQLType = type ~ "[]";
140 			else 
141 				enum SQLType = type;
142 		}
143 	}
144 }
145 
146 unittest
147 {
148 	writeln("\t * SQLType");
149 
150 	static assert(SQLType!int == "INT4");
151 	static assert(SQLType!long == "INT8");
152 	static assert(SQLType!float == "FLOAT4");
153 	static assert(SQLType!(int[]) == "INT4[]");
154 	static assert(SQLType!(long[]) == "INT8[]");
155 	static assert(SQLType!(double[]) == "FLOAT8[]");
156 	static assert(SQLType!(string) == "TEXT");
157 	static assert(SQLType!(string[]) == "TEXT[]");
158 	static assert(SQLType!(ubyte[]) == "BYTEA");
159 
160 	static assert(SQLType!(Nullable!int) == "INT4");
161 }
162 
163 /**
164 	Returns the number of dimensions of the given array type
165 
166 	Examples:
167 	-----------------
168 	auto dims = ArrayDimensions!(int[][]);
169 	static assert(dims == 2);
170 	-----------------
171  */
172 template ArrayDimensions(T)
173 {
174 	static if (isArray!T)
175 		enum ArrayDimensions = 1 + ArrayDimensions!(ForeachType!T);
176 	else 
177 		enum ArrayDimensions = 0;
178 }
179 
180 unittest
181 {
182 	writeln("\t * ArrayDimensions");
183 
184 	static assert(ArrayDimensions!int == 0);
185 	static assert(ArrayDimensions!(int[]) == 1);
186 	static assert(ArrayDimensions!(int[][]) == 2);
187 	static assert(ArrayDimensions!(int[][][]) == 3);
188 }
189 
190 
191 /// Removes any Nullable specifiers, even multiple levels
192 template NoNullable(T)
193 {
194 	static if (isInstanceOf!(Nullable, T))
195 		// Nullable nullable? Costs us nothing, so why not
196 		alias NoNullable = NoNullable!(Unqual!(ReturnType!(T.get)));
197 	else
198 		alias NoNullable = T;
199 }
200 
201 /**
202 	Will strip off any Nullable, Typedefs and qualifiers from a given type
203 
204 	Examples:
205 		static assert(RealType!(const Nullable!(immutable int) == int);
206  */
207 template RealType(T)
208 {
209 	import std.typecons : TypedefType;
210 	// Ugly, but better than doing this every time we need it
211 	alias NT = Unqual!(NoNullable!(TypedefType!T));
212 
213 	static if (is(T == NT))
214 		alias RealType = NT;
215 	else
216 		alias RealType = RealType!NT;
217 }
218 
219 template ShouldRecurse(alias TA)
220 {
221 	alias T = NoNullable!(typeof(TA));
222 	static if (is(T == class) || is(T == struct))
223 	{
224 		static if (hasUDA!(TA, EmbedAttribute))
225 			enum ShouldRecurse = true;
226 		else
227 			enum ShouldRecurse = false;
228 	}
229 	else
230 		enum ShouldRecurse = false;
231 }