1 2 module dquery.attributes; 3 4 import std.typetuple; 5 6 import dquery.attribute; 7 import dquery.helper; 8 9 struct DQueryAttributes(QueryType, Attributes...) 10 { 11 12 alias attributes this; 13 14 /++ 15 + Returns the type being queried. 16 ++/ 17 @property 18 alias type = QueryType; 19 20 /++ 21 + Returns a tuple of attributes in the query. 22 ++/ 23 @property 24 alias attributes = Attributes; 25 26 /++ 27 + Returns true if the list of attributes is empty. 28 ++/ 29 @property 30 alias empty = Alias!(length == 0); 31 32 /++ 33 + Returns the number of attributes in the query. 34 ++/ 35 @property 36 alias length = Alias!(Attributes.length); 37 38 /++ 39 + Returns a transformed list of attributes with all duplicateds removed. 40 ++/ 41 @property 42 alias unique = Alias!( 43 DQueryAttributes!(QueryType, NoDuplicates!Attributes)() 44 ); 45 46 /++ 47 + Returns an uninitialized value of the query's type. 48 ++/ 49 @property 50 static auto opCall() 51 { 52 DQueryAttributes!(QueryType, Attributes) attributes = void; 53 return attributes; 54 } 55 56 /++ 57 + Returns a query for the type that produced this attribute query. 58 ++/ 59 @property 60 static auto parent()() 61 { 62 import dquery.d; 63 return query!QueryType; 64 } 65 66 /++ 67 + Returns the first attribute in the query. 68 ++/ 69 @property 70 static auto first()() 71 if(!empty) 72 { 73 return Attributes[0]; 74 } 75 76 /++ 77 + Returns the first attribute in the query, or a fallback if empty. 78 ++/ 79 @property 80 static auto firstOr(alias Fallback)() 81 if(!is(typeof(Fallback) == void)) 82 { 83 static if(!empty) 84 { 85 return Attributes[0]; 86 } 87 else 88 { 89 return Fallback; 90 } 91 } 92 93 /++ 94 + Returns true if all of the given attributes are present. 95 ++/ 96 @property 97 static auto hasAllOf(TList...)() 98 if(TList.length > 0) 99 { 100 auto query = DQueryAttributes!(QueryType, Attributes)(); 101 102 static if(TList.length > 1) 103 { 104 return query.hasAnyOf!(TList[0]) && query.hasAllOf!(TList[1 .. $]); 105 } 106 else 107 { 108 return query.hasAnyOf!(TList[0]); 109 } 110 } 111 112 /++ 113 + Returns true if any of the given attributes are present. 114 ++/ 115 @property 116 static auto hasAnyOf(TList...)() 117 if(TList.length > 0) 118 { 119 auto query = DQueryAttributes!(QueryType, Attributes)(); 120 return !query.anyOf!TList.empty; 121 } 122 123 /++ 124 + Returns true if none of the given attributes are present. 125 ++/ 126 @property 127 static auto hasNoneOf(TList...)() 128 if(TList.length > 0) 129 { 130 auto query = DQueryAttributes!(QueryType, Attributes)(); 131 return query.anyOf!TList.empty; 132 } 133 134 /++ 135 + Returns a subset of the list of attributes which match 136 + at least one of the given types. 137 ++/ 138 @property 139 static auto anyOf(TList...)() 140 if(TList.length > 0) 141 { 142 alias AnyOfFilter(Type) = Alias!( 143 UnaryToPred!(attribute => attribute.isTypeOf!Type) 144 ); 145 146 auto query = DQueryAttributes!(QueryType, Attributes)(); 147 148 static if(Attributes.length > 0) 149 { 150 alias Pred = templateOr!(staticMap!(AnyOfFilter, TList)); 151 return query.filter!Pred; 152 } 153 else 154 { 155 return query; 156 } 157 } 158 159 /++ 160 + Property that returns a subset of the list of attributes 161 + which match none of the given types. 162 ++/ 163 @property 164 static auto noneOf(TList...)() 165 if(TList.length > 0) 166 { 167 alias NoneOfFilter(Type) = Alias!( 168 UnaryToPred!(attribute => !attribute.isTypeOf!Type) 169 ); 170 171 auto query = DQueryAttributes!(QueryType, Attributes)(); 172 173 static if(Attributes.length > 0) 174 { 175 alias Pred = templateAnd!(staticMap!(NoneOfFilter, TList)); 176 return query.filter!Pred; 177 } 178 else 179 { 180 return query; 181 } 182 } 183 184 /++ 185 + Provides query validation functions tied to length. 186 ++/ 187 @property 188 template ensure(string Attr : "length") 189 { 190 import std.conv : text; 191 192 @property 193 static auto minimum(size_t Min, 194 string Message = "Length cannot be less than " ~ Min.text)() 195 { 196 static assert(length >= Min, Message); 197 return DQueryAttributes!(QueryType, Attributes)(); 198 } 199 200 @property 201 static auto maximum(size_t Max, 202 string Message = "Length cannot be greater than " ~ Max.text)() 203 { 204 static assert(length <= Max, Message); 205 return DQueryAttributes!(QueryType, Attributes)(); 206 } 207 208 @property 209 static auto between(size_t Min, size_t Max, 210 string Message = "Length must be between " ~ Min.text ~ " and " ~ Max.text)() 211 { 212 static assert(length >= Min && length <= Max, Message); 213 return DQueryAttributes!(QueryType, Attributes)(); 214 } 215 216 @property 217 static auto exactly(size_t Length, 218 string Message = "Length must be exactly " ~ Length.text)() 219 { 220 static assert(length == Length, Message); 221 return DQueryAttributes!(QueryType, Attributes)(); 222 } 223 } 224 225 } 226 227 template filter(alias Pred) 228 { 229 @property 230 auto filter(QueryType, Attributes...)(DQueryAttributes!(QueryType, Attributes) attributes) 231 if(Attributes.length == 0) 232 { 233 return attributes; 234 } 235 236 @property 237 auto filter(QueryType, Attributes...)(DQueryAttributes!(QueryType, Attributes) attributes) 238 if(Attributes.length > 0 && __traits(compiles, { 239 DQueryAttributes!(QueryType, Filter!(UnaryToPred!Pred, Attributes)) result = void; 240 })) 241 { 242 DQueryAttributes!(QueryType, Filter!(UnaryToPred!Pred, Attributes)) result = void; 243 return result; 244 } 245 246 @property 247 auto filter(QueryType, Attributes...)(DQueryAttributes!(QueryType, Attributes) attributes) 248 if(Attributes.length > 0 && __traits(compiles, { 249 DQueryAttributes!(QueryType, Filter!(Pred, Attributes)) result = void; 250 })) 251 { 252 DQueryAttributes!(QueryType, Filter!(Pred, Attributes)) result = void; 253 return result; 254 } 255 }