1 module plist.conv; 2 import plist.types; 3 import dxml.dom; 4 import std.traits : EnumMembers, isAssignable; 5 6 bool typeIsCoercible(T)(PlistElementType type) { 7 // Can't avoid boilerplate here, as we need to hardcode the actual element types 8 if (type == PlistElementType.PLIST_ELEMENT_DATA) { 9 if (isAssignable!(T, ubyte[])) { 10 return true; 11 } 12 } 13 14 if (type == PlistElementType.PLIST_ELEMENT_STRING || 15 type == PlistElementType.PLIST_ELEMENT_KEY) { 16 if (isAssignable!(T, string)) { 17 return true; 18 } 19 } 20 21 if (type == PlistElementType.PLIST_ELEMENT_DATE) { 22 import std.datetime : SysTime; 23 if (isAssignable!(T, SysTime)) { 24 return true; 25 } 26 } 27 28 if (type == PlistElementType.PLIST_ELEMENT_BOOLEAN_TRUE || 29 type == PlistElementType.PLIST_ELEMENT_BOOLEAN_FALSE) { 30 if (isAssignable!(T, bool)) { 31 return true; 32 } 33 } 34 35 if (type == PlistElementType.PLIST_ELEMENT_INTEGER) { 36 if (isAssignable!(T, long)) { 37 return true; 38 } 39 } 40 41 if (type == PlistElementType.PLIST_ELEMENT_REAL) { 42 if (is(T == real)) { 43 return true; 44 } 45 } 46 47 return false; 48 } 49 50 bool validateDataType(DOMEntity!string _entity) { 51 import std.algorithm.searching; 52 53 auto members = cast(string[])[EnumMembers!PlistElementType]; // this makes a compile-time array of every value possible in the enum 54 if (members.canFind(_entity.name)) { // verify that it's one of them 55 return true; 56 } 57 return false; 58 } 59 60 PlistElement coerce(T)(DOMEntity!string _entity) { 61 auto element = new T(); // object creation needed because type() cannot be static 62 if (_entity.name != "true" && _entity.name != "false" && _entity.name != "key") { // booleans are the exception, <bool>false</bool> is just <false/> 63 assert(_entity.name == element.type(), "Entity name " ~ _entity.name ~ " did not match " ~ element.type()); 64 } 65 66 element.instantiate(_entity); 67 68 return element; 69 } 70 71 PlistElement coerceRuntime(PlistElementType type, DOMEntity!string _entity) { 72 assert(_entity.type == EntityType.elementStart || _entity.type == EntityType.elementEmpty, "Encountered invalid type"); // this should never happen 73 assert(_entity.name == type); 74 assert(validateDataType(_entity), "Invalid type " ~ _entity.name); 75 76 PlistElement element; 77 78 79 static foreach(etype; [EnumMembers!PlistElementType]) { 80 if (type == etype) { // Check has to be done at runtime 81 mixin("element = coerce!(" ~ getElementClassFromType(etype) ~ ")(_entity);"); 82 } 83 } 84 85 return element; 86 } 87 88 auto getElementClassFromType(PlistElementType type) { 89 static foreach(etype; [EnumMembers!PlistElementType]) { 90 if (type == etype) { 91 static if (etype == PlistElementType.PLIST_ELEMENT_BOOLEAN_TRUE || etype == PlistElementType.PLIST_ELEMENT_BOOLEAN_FALSE) { 92 return "PlistElementBoolean"; 93 } else static if (etype == PlistElementType.PLIST_ELEMENT_KEY) { 94 return "PlistElementString"; 95 } else { 96 import std.string : capitalize; 97 return "PlistElement" ~ capitalize(cast(string)etype); 98 } 99 } 100 } 101 102 assert(0, "Should never reach this point"); 103 }