1 module types; 2 3 import boilerplate; 4 import std.algorithm; 5 import std.array; 6 import std.format; 7 import std.json; 8 import std.range; 9 import std.typecons; 10 import text.json.Decode; 11 import ToJson; 12 13 abstract class Type 14 { 15 @(This.Exclude) 16 string source; 17 18 void setSource(string source) 19 { 20 this.source = source; 21 } 22 23 @(This.Default) 24 string description; 25 26 // are we monadic yet 27 abstract Type transform(Type delegate(Type) dg); 28 29 mixin(GenerateAll); 30 } 31 32 Type decode(T)(const JSONValue value) 33 if (is(T == Type)) 34 in (value.type == JSONType.array) 35 { 36 string type = value.hasKey("type") ? value.getEntry("type").str : null; 37 if (value.hasKey("allOf")) 38 { 39 const description = value.hasKey("description") ? value.getEntry("description").str : null; 40 41 return new AllOf(value.getEntry("allOf").decodeJson!(Type[], .decode), description); 42 } 43 if (value.hasKey("oneOf")) 44 { 45 const description = value.hasKey("description") ? value.getEntry("description").str : null; 46 47 return new OneOf(value.getEntry("oneOf").decodeJson!(Type[], .decode), description); 48 } 49 if (value.hasKey("$ref")) 50 { 51 return new Reference(value.getEntry("$ref").decodeJson!string); 52 } 53 if (type == "object" || type.empty && value.hasKey("properties")) 54 { 55 return value.toObject.decodeJson!(ObjectType, .decode); 56 } 57 if (type == "string") 58 { 59 return value.toObject.decodeJson!(StringType, .decode); 60 } 61 if (value.hasKey("enum")) 62 { 63 return new EnumType(value.getEntry("enum").decodeJson!(string[])); 64 } 65 if (type == "array") 66 { 67 return value.toObject.decodeJson!(ArrayType, .decode); 68 } 69 if (type == "bool" || type == "boolean") 70 { 71 return value.toObject.decodeJson!(BooleanType, .decode); 72 } 73 if (type == "number") 74 { 75 return value.toObject.decodeJson!(NumberType, .decode); 76 } 77 if (type == "integer") 78 { 79 return value.toObject.decodeJson!(IntegerType, .decode); 80 } 81 assert(false, format!"I don't know what this is: %s"(value)); 82 } 83 84 AdditionalProperties decode(T : AdditionalProperties)(const JSONValue value) 85 in (value.type == JSONType.array) 86 { 87 auto type = decode!Type(value); 88 auto additionalProperties = Nullable!int(); 89 90 if (value.hasKey("minProperties")) 91 { 92 additionalProperties = value.getEntry("minProperties").decodeJson!int; 93 } 94 return new AdditionalProperties(type, additionalProperties); 95 } 96 97 private alias _ = decode!Type; 98 private alias _ = decode!AdditionalProperties; 99 100 struct TableEntry(T) 101 { 102 string key; 103 104 T value; 105 106 mixin(GenerateAll); 107 } 108 109 class ObjectType : Type 110 { 111 @(This.Default!null) 112 TableEntry!Type[] properties; 113 114 @(This.Default!null) 115 string[] required; 116 117 @(This.Default) 118 Nullable!AdditionalProperties additionalProperties; 119 120 override void setSource(string source) 121 { 122 super.setSource(source); 123 foreach (entry; properties) 124 { 125 entry.value.setSource(source); 126 } 127 } 128 129 Type findKey(string key) 130 { 131 return properties 132 .filter!(a => a.key == key) 133 .frontOrNull 134 .apply!"a.value" 135 .get(null); 136 } 137 138 override Type transform(Type delegate(Type) dg) 139 { 140 with (ObjectType.Builder()) 141 { 142 properties = this.properties 143 .map!(a => TableEntry!Type(a.key, dg(a.value))) 144 .array; 145 required = this.required; 146 source = this.source; 147 description = this.description; 148 additionalProperties = this.additionalProperties; 149 return value; 150 } 151 } 152 153 mixin(GenerateAll); 154 } 155 156 private alias frontOrNull = range => range.empty ? Nullable!(ElementType!(typeof(range)))() : range.front.nullable; 157 158 class AdditionalProperties 159 { 160 Type type; 161 162 @(This.Default) 163 Nullable!int minProperties; 164 165 mixin(GenerateAll); 166 } 167 168 class ArrayType : Type 169 { 170 Type items; 171 172 @(This.Default) 173 Nullable!int minItems; 174 175 override void setSource(string source) 176 { 177 super.setSource(source); 178 this.items.setSource(source); 179 } 180 181 override Type transform(Type delegate(Type) dg) 182 { 183 auto transformed = new ArrayType(dg(this.items), this.minItems, this.description); 184 transformed.setSource(this.source); 185 return transformed; 186 } 187 188 mixin(GenerateAll); 189 } 190 191 class StringType : Type 192 { 193 @(This.Default) 194 string[] enum_; 195 196 @(This.Default) 197 Nullable!string format_; 198 199 @(This.Default) 200 Nullable!int minLength; 201 202 override Type transform(Type delegate(Type) dg) 203 { 204 return this; 205 } 206 207 mixin(GenerateAll); 208 } 209 210 class EnumType : Type 211 { 212 string[] entries; 213 214 override Type transform(Type delegate(Type) dg) 215 { 216 return this; 217 } 218 219 mixin(GenerateAll); 220 } 221 222 class BooleanType : Type 223 { 224 @(This.Default) 225 Nullable!bool default_; 226 227 override Type transform(Type delegate(Type) dg) 228 { 229 return this; 230 } 231 232 mixin(GenerateAll); 233 } 234 235 class NumberType : Type 236 { 237 override Type transform(Type delegate(Type) dg) 238 { 239 return this; 240 } 241 242 mixin(GenerateAll); 243 } 244 245 class IntegerType : Type 246 { 247 @(This.Default) 248 Nullable!string format_; 249 250 override Type transform(Type delegate(Type) dg) 251 { 252 return this; 253 } 254 255 public string toDType() 256 { 257 switch (this.format_.get("")) 258 { 259 case "int8": return "const(ubyte)"; 260 case "int16": return "short"; 261 case "int32": return "int"; 262 case "int64": return "long"; 263 default: return "long"; 264 } 265 } 266 267 mixin(GenerateAll); 268 } 269 270 class AllOf : Type 271 { 272 Type[] children; 273 274 override void setSource(string source) 275 { 276 super.setSource(source); 277 foreach (child; children) 278 { 279 child.setSource(source); 280 } 281 } 282 283 override Type transform(Type delegate(Type) dg) 284 { 285 auto transformed = new AllOf(this.children.map!(a => dg(a)).array, this.description); 286 transformed.setSource(this.source); 287 return transformed; 288 } 289 290 mixin(GenerateAll); 291 } 292 293 class OneOf : Type 294 { 295 Type[] children; 296 297 override void setSource(string source) 298 { 299 super.setSource(source); 300 foreach (child; children) 301 { 302 child.setSource(source); 303 } 304 } 305 306 override Type transform(Type delegate(Type) dg) 307 { 308 auto transformed = new OneOf(this.children.map!(a => dg(a)).array, this.description); 309 transformed.setSource(this.source); 310 return transformed; 311 } 312 313 mixin(GenerateAll); 314 } 315 316 class Reference : Type 317 { 318 string target; 319 320 override Type transform(Type delegate(Type) dg) 321 { 322 return this; 323 } 324 325 mixin(GenerateAll); 326 }