import std.ascii; import std.stdio; import std.string; import yaml; struct Color { ubyte red; ubyte green; ubyte blue; } Color constructColorScalar(ref Node node) { string value = node.as!string; if(value.length != 6) { throw new Exception("Invalid color: " ~ value); } //We don't need to check for uppercase chars this way. value = value.toLower(); //Get value of a hex digit. uint hex(char c) { if(!std.ascii.isHexDigit(c)) { throw new Exception("Invalid color: " ~ value); } if(std.ascii.isDigit(c)) { return c - '0'; } return c - 'a' + 10; } Color result; result.red = cast(ubyte)(16 * hex(value[0]) + hex(value[1])); result.green = cast(ubyte)(16 * hex(value[2]) + hex(value[3])); result.blue = cast(ubyte)(16 * hex(value[4]) + hex(value[5])); return result; } Color constructColorMapping(ref Node node) { ubyte r,g,b; //Might throw if a value is missing is not an integer, or is out of range. //If this happens, D:YAML will handle the exception and use its message //in a YAMLException thrown when loading. r = node["r"].as!ubyte; g = node["g"].as!ubyte; b = node["b"].as!ubyte; return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b); } void main() { auto red = Color(255, 0, 0); auto orange = Color(255, 255, 0); try { auto constructor = new Constructor; //both functions handle the same tag, but one handles scalar, one mapping. constructor.addConstructorScalar("!color", &constructColorScalar); constructor.addConstructorMapping("!color-mapping", &constructColorMapping); auto loader = Loader("input.yaml"); loader.constructor = constructor; auto root = loader.load(); if(root["scalar-red"].as!Color == red && root["mapping-red"].as!Color == red && root["scalar-orange"].as!Color == orange && root["mapping-orange"].as!Color == orange) { writeln("SUCCESS"); return; } } catch(YAMLException e) { writeln(e.msg); } writeln("FAILURE"); }