1 /** 2 * Copyright © DiamondMVC 2019 3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module diamond.seo.schema.schemaobject; 7 8 import diamond.core.apptype; 9 10 static if (isWeb) 11 { 12 import std.variant; 13 14 /// Wrapper around a schema object. 15 abstract class SchemaObject 16 { 17 private: 18 /// The type of the schema object. 19 string _type; 20 /// Boolean determining whether the object is a child or not. 21 package(diamond) bool _isChild; 22 /// ALl fields attached to the schema object. 23 Variant[string] _fields; 24 25 public: 26 final: 27 /** 28 * Creates a new schema object. 29 * Params: 30 * type = The type of the schema object. Equivalent to "@type" 31 */ 32 this(string type) 33 { 34 _type = type; 35 } 36 37 @property 38 { 39 /// Gets a boolean determining whether the schema object is a root object or not. 40 bool isRoot() { return !_isChild; } 41 } 42 43 /** 44 * Adds a schema object field to the schema object. 45 * Params: 46 * key = The key of the field. 47 * schemaObject = The schema object to add. 48 */ 49 void addField(T : SchemaObject)(string key, T schemaObject) 50 { 51 auto schema = cast(SchemaObject)schemaObject; 52 53 if (schema) 54 { 55 schema._isChild = true; 56 } 57 58 _fields[key] = schema; 59 } 60 61 /** 62 * Adds an array of schema objects to the schema object. 63 * Params: 64 * key = The key of the field. 65 * values = An array of schema objects to add. 66 */ 67 void addField(T : SchemaObject)(string key, T[] values) 68 { 69 SchemaObject[] arrayResult; 70 71 if (values) 72 { 73 foreach (child; values) 74 { 75 child._isChild = true; 76 77 arrayResult ~= child; 78 } 79 80 _fields[key] = arrayResult ? arrayResult : []; 81 } 82 else 83 { 84 _fields[key] = arrayResult; 85 } 86 } 87 88 /** 89 * Adds a field to the schema object. 90 * Params: 91 * key = The key of the field. 92 * value = The value of the field. 93 */ 94 void addField(T)(string key, T value) 95 { 96 _fields[key] = value; 97 } 98 99 /** 100 * Removes a field from the schema object. 101 * Params: 102 * key = The key of the field to remove. 103 */ 104 void removeField(string key) 105 { 106 _fields.remove(key); 107 } 108 109 /// Converts the schema object to a string that represent a JSON-LD object. 110 override string toString() 111 { 112 import std..string : format; 113 import std.conv : to; 114 import std.array : join; 115 116 string[] fieldsValue; 117 118 if (_fields && _fields.length) 119 { 120 foreach (k,v; _fields) 121 { 122 if (!v.hasValue) 123 { 124 continue; 125 } 126 127 if (v.convertsTo!SchemaObject) 128 { 129 auto schemaObject = v.get!SchemaObject; 130 131 fieldsValue ~= "\"%s\": %s".format(k,schemaObject.toString()); 132 } 133 else if (v.convertsTo!(SchemaObject[])) 134 { 135 auto schemaObjects = v.get!(SchemaObject[]); 136 137 if (schemaObjects) 138 { 139 fieldsValue ~= "\"%s\": %s".format(k,to!string(schemaObjects)); 140 } 141 else 142 { 143 fieldsValue ~= "\"%s\": null".format(k); 144 } 145 } 146 else if (v.convertsTo!string) 147 { 148 auto stringValue = v.get!string; 149 150 fieldsValue ~= "\"%s\": \"%s\"".format(k,stringValue); 151 } 152 else 153 { 154 fieldsValue ~= "\"%s\": %s".format(k,v.toString()); 155 } 156 } 157 } 158 159 if (!_isChild) 160 { 161 return `{ 162 "@context" : "http://schema.org", 163 "@type": "%s" 164 %s 165 }`.format 166 ( 167 _type, 168 fieldsValue && fieldsValue.length ? (", " ~ fieldsValue.join(",\r\n")) : "" 169 ); 170 } 171 else 172 { 173 return `{ 174 "@type": "%s" 175 %s 176 }`.format 177 ( 178 _type, 179 fieldsValue && fieldsValue.length ? (", " ~ fieldsValue.join(",\r\n")) : "" 180 ); 181 } 182 } 183 } 184 }