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