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.convertor.setter; 31 32 import aermicioi.aedi : NotFoundException; 33 import aermicioi.aedi_property_reader.convertor.exception; 34 import aermicioi.aedi_property_reader.convertor.convertor; 35 import aermicioi.aedi_property_reader.convertor.placeholder; 36 import aermicioi.aedi.util.traits : isPublic, isField; 37 import aermicioi.aedi_property_reader.convertor.traits; 38 import std.conv; 39 import std.traits; 40 import std.meta; 41 import std.exception; 42 import std.experimental.allocator; 43 44 /** 45 Provides ability to set a property of FieldType into CompositeType. 46 **/ 47 interface PropertySetter(CompositeType, FieldType = CompositeType, KeyType = string) { 48 49 /** 50 Set a field or property of CompositeType. 51 52 Params: 53 composite = composite that will store value 54 value = actual value that is assigned to a field in composite 55 property = the identity of field in composite 56 Throws: 57 InvalidArgumentException when value or composite is not what was expected 58 **/ 59 void set(ref CompositeType composite, FieldType value, KeyType property, RCIAllocator allocator = theAllocator) const; 60 61 /** 62 Identify the type of supported component. 63 64 Identify the type of supported component. It returns type info of component 65 if it is supported by accessor, otherwise it will return typeid(void) denoting that 66 the type isn't supported by accessor. The accessor is not limited to returning the type 67 info of passed component, it can actually return type info of super type or any type 68 given the returned type is implicitly convertible or castable to ComponentType. 69 70 Params: 71 component = the component for which accessor should identify the underlying type 72 73 Returns: 74 TypeInfo type information about passed component, or typeid(void) if component is not supported. 75 **/ 76 TypeInfo componentType(ref CompositeType composite) const nothrow; 77 78 /** 79 Identify the type of supported component. 80 81 Identify the type of supported component. It returns type info of component 82 if it is supported by accessor, otherwise it will return typeid(void) denoting that 83 the type isn't supported by accessor. The accessor is not limited to returning the type 84 info of passed component, it can actually return type info of super type or any type 85 given the returned type is implicitly convertible or castable to ComponentType. 86 87 Params: 88 component = the component for which accessor should identify the underlying type 89 90 Returns: 91 TypeInfo type information about passed component, or typeid(void) if component is not supported. 92 **/ 93 final TypeInfo componentType(CompositeType composite) const nothrow { 94 return this.componentType(composite); 95 } 96 } 97 98 /** 99 Associative array setter. 100 **/ 101 class AssociativeArraySetter(Type, KeyType = Type) : PropertySetter!(Type[KeyType], Type, KeyType) { 102 103 public { 104 /** 105 Set a field or property of CompositeType. 106 107 Params: 108 composite = composite that will store value 109 value = actual value that is assigned to a field in composite 110 property = the identity of field in composite 111 Throws: 112 InvalidArgumentException when value or composite is not what was expected 113 **/ 114 void set(ref Type[KeyType] composite, Type field, KeyType key, RCIAllocator allocator = theAllocator) const { 115 composite[key] = field; 116 } 117 118 /** 119 Identify the type of supported component. 120 121 Identify the type of supported component. It returns type info of component 122 if it is supported by accessor, otherwise it will return typeid(void) denoting that 123 the type isn't supported by accessor. The accessor is not limited to returning the type 124 info of passed component, it can actually return type info of super type or any type 125 given the returned type is implicitly convertible or castable to ComponentType. 126 127 Params: 128 component = the component for which accessor should identify the underlying type 129 130 Returns: 131 TypeInfo type information about passed component, or typeid(void) if component is not supported. 132 **/ 133 TypeInfo componentType(ref Type[KeyType] composite) const nothrow { 134 return typeid(Type[KeyType]); 135 } 136 } 137 } 138 139 /** 140 Array setter. 141 **/ 142 class ArraySetter(Type) : PropertySetter!(Type[], Type, size_t) { 143 144 /** 145 Set a field or property of CompositeType. 146 147 Params: 148 composite = composite that will store value 149 value = actual value that is assigned to a field in composite 150 property = the identity of field in composite 151 Throws: 152 InvalidArgumentException when value or composite is not what was expected 153 **/ 154 void set(ref Type[] composite, Type field, size_t key, RCIAllocator allocator = theAllocator) const { 155 enforce!InvalidArgumentException(key < composite.length, text( 156 "Cannot assign ", 157 field, 158 " to ", 159 composite, 160 " index ", 161 key, 162 " out of bounds ", 163 composite.length 164 )); 165 166 composite[key] = field; 167 } 168 169 /** 170 Identify the type of supported component. 171 172 Identify the type of supported component. It returns type info of component 173 if it is supported by accessor, otherwise it will return typeid(void) denoting that 174 the type isn't supported by accessor. The accessor is not limited to returning the type 175 info of passed component, it can actually return type info of super type or any type 176 given the returned type is implicitly convertible or castable to ComponentType. 177 178 Params: 179 component = the component for which accessor should identify the underlying type 180 181 Returns: 182 TypeInfo type information about passed component, or typeid(void) if component is not supported. 183 **/ 184 TypeInfo componentType(ref Type[] composite) const nothrow { 185 return typeid(Type[]); 186 } 187 } 188 189 /** 190 Composite (object, struct, union) setter. 191 **/ 192 class CompositeSetter(ComponentType) : PropertySetter!(ComponentType, Object, string) 193 if (isAggregateType!ComponentType) { 194 195 /** 196 Set a field or property of CompositeType. 197 198 Params: 199 composite = composite that will store value 200 value = actual value that is assigned to a field in composite 201 property = the identity of field in composite 202 Throws: 203 InvalidArgumentException when value or composite is not what was expected 204 **/ 205 void set(T)(ref ComponentType component, T field, string property, RCIAllocator allocator = theAllocator) const { 206 import std.experimental.allocator : theAllocator, dispose; 207 auto wrapped = field.stored; 208 this.set(component, wrapped, property); 209 } 210 211 /** 212 ditto 213 **/ 214 void set(ref ComponentType component, Object field, string property, RCIAllocator allocator = theAllocator) const { 215 static foreach (member; __traits(allMembers, ComponentType)) {{ 216 static if (isPublic!(ComponentType, member)) { 217 alias m = Alias!(__traits(getMember, component, member)); 218 219 if (member == property) { 220 static if (isField!(ComponentType, member)) { 221 alias FieldType = typeof(__traits(getMember, component, member)); 222 223 enforce!InvalidArgumentException(field.identify is typeid(FieldType), text( 224 "Cannot set value of type ", field.identify, 225 " to property ", member, 226 " of type ", typeid(FieldType) 227 )); 228 229 __traits(getMember, component, member) = field.unwrap!FieldType; 230 return; 231 } else static if ( 232 isSomeFunction!m && 233 anySatisfy!(isPropertyPropertySetter, __traits(getOverloads, ComponentType, member)) 234 ) { 235 alias FieldType = Parameters!( 236 match!( 237 isPropertyPropertySetter, 238 __traits(getOverloads, ComponentType, member) 239 ) 240 )[0]; 241 242 enforce!InvalidArgumentException(field.identify is typeid(FieldType), text( 243 "Cannot set value of type ", field.identify, 244 " to property ", member, 245 " of type ", typeid(FieldType) 246 )); 247 248 __traits(getMember, component, member) = field.unwrap!FieldType; 249 return; 250 } 251 } 252 } 253 }} 254 255 throw new NotFoundException(text( 256 "Component of type ", 257 typeid(ComponentType), 258 " does not have ${identity} property" 259 ), property); 260 } 261 262 /** 263 Identify the type of supported component. 264 265 Identify the type of supported component. It returns type info of component 266 if it is supported by accessor, otherwise it will return typeid(void) denoting that 267 the type isn't supported by accessor. The accessor is not limited to returning the type 268 info of passed component, it can actually return type info of super type or any type 269 given the returned type is implicitly convertible or castable to ComponentType. 270 271 Params: 272 component = the component for which accessor should identify the underlying type 273 274 Returns: 275 TypeInfo type information about passed component, or typeid(void) if component is not supported. 276 **/ 277 TypeInfo componentType(ref ComponentType composite) const nothrow { 278 return typeid(ComponentType); 279 } 280 } 281 282 /** 283 Runtime composite setter that accepts erased Type, restores it's type and sets data in it. 284 **/ 285 class RuntimeCompositeSetter(Type, FieldType = Type, KeyType = string) : PropertySetter!(Object, FieldType, KeyType) { 286 private { 287 PropertySetter!(Type, FieldType, KeyType) setter_; 288 } 289 290 public { 291 /** 292 Constructor for runtime composite setter. 293 294 Params: 295 setter = underlying setter used for assigning fields to component 296 **/ 297 this(PropertySetter!(Type, FieldType, KeyType) setter) { 298 this.setter = setter; 299 } 300 301 @property { 302 /** 303 Set setter 304 305 Params: 306 setter = underlying setter used to set data 307 308 Returns: 309 typeof(this) 310 **/ 311 typeof(this) setter(PropertySetter!(Type, FieldType, KeyType) setter) @safe nothrow pure { 312 this.setter_ = setter; 313 314 return this; 315 } 316 317 /** 318 Get setter 319 320 Returns: 321 PropertySetter!(Type, FieldType, KeyType) 322 **/ 323 inout(PropertySetter!(Type, FieldType, KeyType)) setter() @safe nothrow pure inout { 324 return this.setter_; 325 } 326 } 327 328 /** 329 Set a field or property of CompositeType. 330 331 Params: 332 composite = composite that will store value 333 value = actual value that is assigned to a field in composite 334 property = the identity of field in composite 335 Throws: 336 InvalidArgumentException when value or composite is not what was expected 337 **/ 338 void set(ref Object composite, FieldType field, KeyType key, RCIAllocator allocator = theAllocator) const { 339 this.setter.set(composite.unwrap!Type, field, key); 340 } 341 342 /** 343 Identify the type of supported component. 344 345 Identify the type of supported component. It returns type info of component 346 if it is supported by accessor, otherwise it will return typeid(void) denoting that 347 the type isn't supported by accessor. The accessor is not limited to returning the type 348 info of passed component, it can actually return type info of super type or any type 349 given the returned type is implicitly convertible or castable to ComponentType. 350 351 Params: 352 component = the component for which accessor should identify the underlying type 353 354 Returns: 355 TypeInfo type information about passed component, or typeid(void) if component is not supported. 356 **/ 357 TypeInfo componentType(ref Object composite) const nothrow { 358 if (composite.identify is typeid(Type)) { 359 return this.setter.componentType(composite.unwrap!Type); 360 } 361 362 return typeid(void); 363 } 364 } 365 } 366 367 /** 368 Runtime field setter that accepts type erased fields, that are restored and then assigned to component. 369 **/ 370 class RuntimeFieldSetter(Type, FieldType = Type, KeyType = string) : PropertySetter!(Type, Object, KeyType) { 371 private { 372 PropertySetter!(Type, FieldType, KeyType) setter_; 373 } 374 375 public { 376 /** 377 Constructor for runtime field setter. 378 379 Params: 380 setter = underlying setter that works directly with FieldType values 381 **/ 382 this(PropertySetter!(Type, FieldType, KeyType) setter) { 383 this.setter = setter; 384 } 385 386 @property { 387 /** 388 Set setter 389 390 Params: 391 setter = underlying setter used to set data 392 393 Returns: 394 typeof(this) 395 **/ 396 typeof(this) setter(PropertySetter!(Type, FieldType, KeyType) setter) @safe nothrow pure { 397 this.setter_ = setter; 398 399 return this; 400 } 401 402 /** 403 Get setter 404 405 Returns: 406 PropertySetter!(Type, FieldType, KeyType) 407 **/ 408 inout(PropertySetter!(Type, FieldType, KeyType)) setter() @safe nothrow pure inout { 409 return this.setter_; 410 } 411 } 412 413 /** 414 Set a field or property of CompositeType. 415 416 Params: 417 composite = composite that will store value 418 value = actual value that is assigned to a field in composite 419 property = the identity of field in composite 420 Throws: 421 InvalidArgumentException when value or composite is not what was expected 422 **/ 423 void set(ref Type composite, Object field, KeyType key, RCIAllocator allocator = theAllocator) const { 424 this.setter.set(composite, field.unwrap!FieldType, key); 425 } 426 427 /** 428 Identify the type of supported component. 429 430 Identify the type of supported component. It returns type info of component 431 if it is supported by accessor, otherwise it will return typeid(void) denoting that 432 the type isn't supported by accessor. The accessor is not limited to returning the type 433 info of passed component, it can actually return type info of super type or any type 434 given the returned type is implicitly convertible or castable to ComponentType. 435 436 Params: 437 component = the component for which accessor should identify the underlying type 438 439 Returns: 440 TypeInfo type information about passed component, or typeid(void) if component is not supported. 441 **/ 442 TypeInfo componentType(ref Type composite) const nothrow { 443 return typeid(Type); 444 } 445 } 446 }