1 /** 2 License: 3 Boost Software License - Version 1.0 - August 17th, 2003 4 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 27 Authors: 28 Alexandru Ermicioi 29 **/ 30 module aermicioi.aedi_property_reader.core.setter; 31 32 import aermicioi.aedi : NotFoundException; 33 import aermicioi.aedi_property_reader.core.exception; 34 import aermicioi.aedi_property_reader.core.convertor; 35 import aermicioi.aedi_property_reader.core.placeholder; 36 import aermicioi.util.traits : isPublic, isField; 37 import aermicioi.aedi_property_reader.core.traits; 38 import std.conv; 39 import std.traits; 40 import std.meta; 41 import std.exception; 42 43 /** 44 Provides ability to set a property of FieldType into CompositeType. 45 **/ 46 interface PropertySetter(CompositeType, FieldType = CompositeType, KeyType = string) { 47 48 /** 49 Set a field or property of CompositeType. 50 51 Params: 52 composite = composite that will store value 53 value = actual value that is assigned to a field in composite 54 property = the identity of field in composite 55 Throws: 56 InvalidArgumentException when value or composite is not what was expected 57 **/ 58 void set(ref CompositeType composite, FieldType value, KeyType property) const; 59 60 /** 61 Identify the type of supported component. 62 63 Identify the type of supported component. It returns type info of component 64 if it is supported by accessor, otherwise it will return typeid(void) denoting that 65 the type isn't supported by accessor. The accessor is not limited to returning the type 66 info of passed component, it can actually return type info of super type or any type 67 given the returned type is implicitly convertible or castable to ComponentType. 68 69 Params: 70 component = the component for which accessor should identify the underlying type 71 72 Returns: 73 TypeInfo type information about passed component, or typeid(void) if component is not supported. 74 **/ 75 TypeInfo componentType(ref CompositeType composite) const nothrow; 76 77 /** 78 Identify the type of supported component. 79 80 Identify the type of supported component. It returns type info of component 81 if it is supported by accessor, otherwise it will return typeid(void) denoting that 82 the type isn't supported by accessor. The accessor is not limited to returning the type 83 info of passed component, it can actually return type info of super type or any type 84 given the returned type is implicitly convertible or castable to ComponentType. 85 86 Params: 87 component = the component for which accessor should identify the underlying type 88 89 Returns: 90 TypeInfo type information about passed component, or typeid(void) if component is not supported. 91 **/ 92 final TypeInfo componentType(CompositeType composite) const nothrow { 93 return this.componentType(composite); 94 } 95 } 96 97 /** 98 Associative array setter. 99 **/ 100 class AssociativeArraySetter(Type, KeyType = Type) : PropertySetter!(Type[KeyType], Type, KeyType) { 101 102 public { 103 /** 104 Set a field or property of CompositeType. 105 106 Params: 107 composite = composite that will store value 108 value = actual value that is assigned to a field in composite 109 property = the identity of field in composite 110 Throws: 111 InvalidArgumentException when value or composite is not what was expected 112 **/ 113 void set(ref Type[KeyType] composite, Type field, KeyType key) const { 114 composite[key] = field; 115 } 116 117 /** 118 Identify the type of supported component. 119 120 Identify the type of supported component. It returns type info of component 121 if it is supported by accessor, otherwise it will return typeid(void) denoting that 122 the type isn't supported by accessor. The accessor is not limited to returning the type 123 info of passed component, it can actually return type info of super type or any type 124 given the returned type is implicitly convertible or castable to ComponentType. 125 126 Params: 127 component = the component for which accessor should identify the underlying type 128 129 Returns: 130 TypeInfo type information about passed component, or typeid(void) if component is not supported. 131 **/ 132 TypeInfo componentType(ref Type[KeyType] composite) const nothrow { 133 return typeid(Type[KeyType]); 134 } 135 } 136 } 137 138 /** 139 Array setter. 140 **/ 141 class ArraySetter(Type) : PropertySetter!(Type[], Type, size_t) { 142 143 /** 144 Set a field or property of CompositeType. 145 146 Params: 147 composite = composite that will store value 148 value = actual value that is assigned to a field in composite 149 property = the identity of field in composite 150 Throws: 151 InvalidArgumentException when value or composite is not what was expected 152 **/ 153 void set(ref Type[] composite, Type field, size_t key) const { 154 enforce!InvalidArgumentException(key < composite.length, text( 155 "Cannot assign ", 156 field, 157 " to ", 158 composite, 159 " index ", 160 key, 161 " out of bounds ", 162 composite.length 163 )); 164 165 composite[key] = field; 166 } 167 168 /** 169 Identify the type of supported component. 170 171 Identify the type of supported component. It returns type info of component 172 if it is supported by accessor, otherwise it will return typeid(void) denoting that 173 the type isn't supported by accessor. The accessor is not limited to returning the type 174 info of passed component, it can actually return type info of super type or any type 175 given the returned type is implicitly convertible or castable to ComponentType. 176 177 Params: 178 component = the component for which accessor should identify the underlying type 179 180 Returns: 181 TypeInfo type information about passed component, or typeid(void) if component is not supported. 182 **/ 183 TypeInfo componentType(ref Type[] composite) const nothrow { 184 return typeid(Type[]); 185 } 186 } 187 188 /** 189 Composite (object, struct, union) setter. 190 **/ 191 class CompositeSetter(ComponentType) : PropertySetter!(ComponentType, Object, string) 192 if (isAggregateType!ComponentType) { 193 194 /** 195 Set a field or property of CompositeType. 196 197 Params: 198 composite = composite that will store value 199 value = actual value that is assigned to a field in composite 200 property = the identity of field in composite 201 Throws: 202 InvalidArgumentException when value or composite is not what was expected 203 **/ 204 void set(T)(ref ComponentType component, T field, string property) const { 205 import std.experimental.allocator : theAllocator, dispose; 206 auto wrapped = field.placeholder(theAllocator); 207 this.set(component, wrapped, property); 208 209 static if (!is(T : Object)) { 210 theAllocator.dispose(wrapped); 211 } 212 } 213 214 /** 215 ditto 216 **/ 217 void set(ref ComponentType component, Object field, string property) const { 218 static foreach (member; __traits(allMembers, ComponentType)) {{ 219 static if (isPublic!(ComponentType, member)) { 220 alias m = Alias!(__traits(getMember, component, member)); 221 222 if (member == property) { 223 static if (isField!(ComponentType, member)) { 224 alias FieldType = typeof(__traits(getMember, component, member)); 225 226 enforce!InvalidArgumentException(field.identify is typeid(FieldType), text( 227 "Cannot set value of type ", field.identify, 228 " to property ", member, 229 " of type ", typeid(FieldType) 230 )); 231 232 __traits(getMember, component, member) = field.unwrap!FieldType; 233 return; 234 } else static if ( 235 isSomeFunction!m && 236 anySatisfy!(isPropertyPropertySetter, __traits(getOverloads, ComponentType, member)) 237 ) { 238 alias FieldType = Parameters!( 239 match!( 240 isPropertyPropertySetter, 241 __traits(getOverloads, ComponentType, member) 242 ) 243 )[0]; 244 245 enforce!InvalidArgumentException(field.identify is typeid(FieldType), text( 246 "Cannot set value of type ", field.identify, 247 " to property ", member, 248 " of type ", typeid(FieldType) 249 )); 250 251 __traits(getMember, component, member) = field.unwrap!FieldType; 252 return; 253 } 254 } 255 } 256 }} 257 258 throw new NotFoundException(text( 259 "Component of type ", 260 typeid(ComponentType), 261 " does not have ", 262 property, 263 " property" 264 )); 265 } 266 267 /** 268 Identify the type of supported component. 269 270 Identify the type of supported component. It returns type info of component 271 if it is supported by accessor, otherwise it will return typeid(void) denoting that 272 the type isn't supported by accessor. The accessor is not limited to returning the type 273 info of passed component, it can actually return type info of super type or any type 274 given the returned type is implicitly convertible or castable to ComponentType. 275 276 Params: 277 component = the component for which accessor should identify the underlying type 278 279 Returns: 280 TypeInfo type information about passed component, or typeid(void) if component is not supported. 281 **/ 282 TypeInfo componentType(ref ComponentType composite) const nothrow { 283 return typeid(ComponentType); 284 } 285 } 286 287 /** 288 Runtime composite setter that accepts erased Type, restores it's type and sets data in it. 289 **/ 290 class RuntimeCompositeSetter(Type, FieldType = Type, KeyType = string) : PropertySetter!(Object, FieldType, KeyType) { 291 private { 292 PropertySetter!(Type, FieldType, KeyType) setter_; 293 } 294 295 public { 296 /** 297 Constructor for runtime composite setter. 298 299 Params: 300 setter = underlying setter used for assigning fields to component 301 **/ 302 this(PropertySetter!(Type, FieldType, KeyType) setter) { 303 this.setter = setter; 304 } 305 306 @property { 307 /** 308 Set setter 309 310 Params: 311 setter = underlying setter used to set data 312 313 Returns: 314 typeof(this) 315 **/ 316 typeof(this) setter(PropertySetter!(Type, FieldType, KeyType) setter) @safe nothrow pure { 317 this.setter_ = setter; 318 319 return this; 320 } 321 322 /** 323 Get setter 324 325 Returns: 326 PropertySetter!(Type, FieldType, KeyType) 327 **/ 328 inout(PropertySetter!(Type, FieldType, KeyType)) setter() @safe nothrow pure inout { 329 return this.setter_; 330 } 331 } 332 333 /** 334 Set a field or property of CompositeType. 335 336 Params: 337 composite = composite that will store value 338 value = actual value that is assigned to a field in composite 339 property = the identity of field in composite 340 Throws: 341 InvalidArgumentException when value or composite is not what was expected 342 **/ 343 void set(ref Object composite, FieldType field, KeyType key) const { 344 auto placeholder = composite.unwrap!Type; 345 this.setter.set(placeholder, field, key); 346 } 347 348 /** 349 Identify the type of supported component. 350 351 Identify the type of supported component. It returns type info of component 352 if it is supported by accessor, otherwise it will return typeid(void) denoting that 353 the type isn't supported by accessor. The accessor is not limited to returning the type 354 info of passed component, it can actually return type info of super type or any type 355 given the returned type is implicitly convertible or castable to ComponentType. 356 357 Params: 358 component = the component for which accessor should identify the underlying type 359 360 Returns: 361 TypeInfo type information about passed component, or typeid(void) if component is not supported. 362 **/ 363 TypeInfo componentType(ref Object composite) const nothrow { 364 if (composite.identify is typeid(Type)) { 365 return this.setter.componentType(composite.unwrap!Type); 366 } 367 368 return typeid(void); 369 } 370 } 371 } 372 373 /** 374 Runtime field setter that accepts type erased fields, that are restored and then assigned to component. 375 **/ 376 class RuntimeFieldSetter(Type, FieldType = Type, KeyType = string) : PropertySetter!(Type, Object, KeyType) { 377 private { 378 PropertySetter!(Type, FieldType, KeyType) setter_; 379 } 380 381 public { 382 /** 383 Constructor for runtime field setter. 384 385 Params: 386 setter = underlying setter that works directly with FieldType values 387 **/ 388 this(PropertySetter!(Type, FieldType, KeyType) setter) { 389 this.setter = setter; 390 } 391 392 @property { 393 /** 394 Set setter 395 396 Params: 397 setter = underlying setter used to set data 398 399 Returns: 400 typeof(this) 401 **/ 402 typeof(this) setter(PropertySetter!(Type, FieldType, KeyType) setter) @safe nothrow pure { 403 this.setter_ = setter; 404 405 return this; 406 } 407 408 /** 409 Get setter 410 411 Returns: 412 PropertySetter!(Type, FieldType, KeyType) 413 **/ 414 inout(PropertySetter!(Type, FieldType, KeyType)) setter() @safe nothrow pure inout { 415 return this.setter_; 416 } 417 } 418 419 /** 420 Set a field or property of CompositeType. 421 422 Params: 423 composite = composite that will store value 424 value = actual value that is assigned to a field in composite 425 property = the identity of field in composite 426 Throws: 427 InvalidArgumentException when value or composite is not what was expected 428 **/ 429 void set(ref Type composite, Object field, KeyType key) const { 430 this.setter.set(composite, field.unwrap!FieldType, key); 431 } 432 433 /** 434 Identify the type of supported component. 435 436 Identify the type of supported component. It returns type info of component 437 if it is supported by accessor, otherwise it will return typeid(void) denoting that 438 the type isn't supported by accessor. The accessor is not limited to returning the type 439 info of passed component, it can actually return type info of super type or any type 440 given the returned type is implicitly convertible or castable to ComponentType. 441 442 Params: 443 component = the component for which accessor should identify the underlying type 444 445 Returns: 446 TypeInfo type information about passed component, or typeid(void) if component is not supported. 447 **/ 448 TypeInfo componentType(ref Type composite) const nothrow { 449 return typeid(Type); 450 } 451 } 452 }