1 /** 2 * Authors: Tomoya Tanjo 3 * Copyright: © 2021 Tomoya Tanjo 4 * License: Apache-2.0 5 */ 6 module salad.schema; 7 8 import dyaml : Node, NodeType; 9 10 import salad.exception; 11 import salad.meta; 12 import salad.canonicalizer; 13 import salad.type; 14 15 import so = salad.schema_original; 16 17 import std.algorithm : map; 18 import std.array : array; 19 import std.typecons : Tuple; 20 21 private Optional!string concat(Optional!(string, string[]) ss) 22 { 23 import std.array : join; 24 25 return ss.match!( 26 (string s) => Optional!string(s), 27 (string[] ss) => Optional!string(ss.join("\n")), 28 none => Optional!string.init, 29 ); 30 } 31 32 private auto canonicalize(Optional!(string, so.JsonldPredicate) jp) 33 { 34 return jp.match!( 35 (string id) { 36 auto pred = new JsonldPredicate; 37 if (id == "@id") 38 { 39 pred._type_ = "@id"; 40 pred.identity_ = true; 41 } 42 else 43 { 44 pred._id_ = id; 45 } 46 return pred; 47 }, 48 (so.JsonldPredicate pred) => new JsonldPredicate(pred), 49 none => null, 50 ); 51 } 52 53 private Either!( 54 PrimitiveType, 55 RecordSchema, 56 EnumSchema, 57 ArraySchema, 58 string)[] canonicalize(Either!( 59 so.PrimitiveType, 60 so.RecordSchema, 61 so.EnumSchema, 62 so.ArraySchema, 63 string, 64 Either!( 65 so.PrimitiveType, 66 so.RecordSchema, 67 so.EnumSchema, 68 so.ArraySchema, 69 string)[] 70 ) type) 71 { 72 import std.range : ElementType; 73 74 alias RetElemType = ElementType!(typeof(return)); 75 76 return type.match!( 77 (so.PrimitiveType pt) => [RetElemType(new PrimitiveType(pt))], 78 (so.RecordSchema rs) => [RetElemType(new RecordSchema(rs))], 79 (so.EnumSchema es) => [RetElemType(new EnumSchema(es))], 80 (so.ArraySchema as) => [RetElemType(new ArraySchema(as))], 81 (string str) => [RetElemType(str)], 82 (Either!( 83 so.PrimitiveType, 84 so.RecordSchema, 85 so.EnumSchema, 86 so.ArraySchema, 87 string)[] types) => types.map!( 88 t => t.match!( 89 (so.PrimitiveType pt) => RetElemType(new PrimitiveType(pt)), 90 (so.RecordSchema rs) => RetElemType(new RecordSchema(rs)), 91 (so.EnumSchema es) => RetElemType(new EnumSchema(es)), 92 (so.ArraySchema as) => RetElemType(new ArraySchema(as)), 93 str => RetElemType(str), 94 ) 95 ).array, 96 ); 97 } 98 99 private auto orDefault(T, U)(T val, U default_) 100 if (isOptional!T && is(T.Types[1] == U)) 101 { 102 return val.match!((U u) => u, none => default_); 103 } 104 105 @documentRoot class SaladRecordSchema 106 { 107 mixin genCanonicalizeBody!( 108 so.SaladRecordSchema, 109 "inVocab", (Optional!bool inVocab) => inVocab.orDefault(true), 110 "fields", (Optional!(so.SaladRecordField[]) fields) => 111 fields.match!((so.SaladRecordField[] fs) => fs.map!(f => new SaladRecordField(f)).array, 112 none => (SaladRecordField[]).init), 113 "doc", (Optional!(string, string[]) doc) => doc.concat, 114 "docChild", (Optional!(string, string[]) doc) => doc.concat, 115 "jsonldPredicate", (Optional!(string, so.JsonldPredicate) jp) => jp.canonicalize, 116 "documentRoot", (Optional!bool documentRoot) => documentRoot.orDefault(false), 117 "abstract", (Optional!bool abstract_) => abstract_.orDefault(false), 118 "extends", (Optional!(string, string[]) extends) => extends.match!((string s) => [s], 119 (string[] ss) => ss, 120 none => (string[]).init), 121 "specialize", (Optional!(so.SpecializeDef[]) specialize) => 122 specialize.match!((so.SpecializeDef[] sd) => sd.map!(s => new SpecializeDef(s)).array, 123 none => (SpecializeDef[]).init), 124 ); 125 } 126 127 class SaladRecordField 128 { 129 mixin genCanonicalizeBody!( 130 so.SaladRecordField, 131 "type", (Either!( 132 so.PrimitiveType, 133 so.RecordSchema, 134 so.EnumSchema, 135 so.ArraySchema, 136 string, 137 Either!( 138 so.PrimitiveType, 139 so.RecordSchema, 140 so.EnumSchema, 141 so.ArraySchema, 142 string)[] 143 ) type) => type.canonicalize, 144 "doc", (Optional!(string, string[]) doc) => doc.concat, 145 "jsonldPredicate", (Optional!(string, so.JsonldPredicate) jp) => jp.canonicalize, 146 "default", (Optional!(so.Any) default_) => default_.match!((so.Any any) => new Any(any), none => null), 147 ); 148 } 149 150 class PrimitiveType 151 { 152 mixin genCanonicalizeBody!(so.PrimitiveType); 153 } 154 155 class Any 156 { 157 mixin genCanonicalizeBody!(so.Any); 158 } 159 160 class RecordSchema 161 { 162 mixin genCanonicalizeBody!( 163 so.RecordSchema, 164 "fields", (Optional!(so.RecordField[]) fields) => 165 fields.match!((so.RecordField[] rf) => rf.map!(r => new RecordField(r)).array, 166 none => (RecordField[]).init), 167 ); 168 } 169 170 class RecordField 171 { 172 mixin genCanonicalizeBody!( 173 so.RecordField, 174 "type", (Either!( 175 so.PrimitiveType, 176 so.RecordSchema, 177 so.EnumSchema, 178 so.ArraySchema, 179 string, 180 Either!( 181 so.PrimitiveType, 182 so.RecordSchema, 183 so.EnumSchema, 184 so.ArraySchema, 185 string)[] 186 ) type) => type.canonicalize, 187 "doc", (Optional!(string, string[]) doc) => doc.concat, 188 ); 189 } 190 191 class EnumSchema 192 { 193 mixin genCanonicalizeBody!(so.EnumSchema); 194 } 195 196 class ArraySchema 197 { 198 mixin genCanonicalizeBody!( 199 so.ArraySchema, 200 "items", (Either!( 201 so.PrimitiveType, 202 so.RecordSchema, 203 so.EnumSchema, 204 so.ArraySchema, 205 string, 206 Either!( 207 so.PrimitiveType, 208 so.RecordSchema, 209 so.EnumSchema, 210 so.ArraySchema, 211 string)[] 212 ) items) => items.canonicalize, 213 ); 214 } 215 216 class JsonldPredicate 217 { 218 mixin genCanonicalizeBody!( 219 so.JsonldPredicate, 220 "_id", (Optional!string _id) => _id.orDefault(""), 221 "_type", (Optional!string _type) => _type.orDefault(""), 222 "_container", (Optional!string _container) => _container.orDefault(""), 223 "identity", (Optional!bool identity) => identity.orDefault(false), 224 "noLinkCheck", (Optional!bool noLinkCheck) => noLinkCheck.orDefault(false), 225 "mapSubject", (Optional!string mapSubject) => mapSubject.orDefault(""), 226 "mapPreidcate", (Optional!string mapPredicate) => mapPredicate.orDefault(""), 227 "refScope", (Optional!int refScope) => refScope.orDefault(0), 228 "typeDSL", (Optional!bool typeDSL) => typeDSL.orDefault(false), 229 "secondaryFilesDSL", (Optional!bool secondaryFilesDSL) => secondaryFilesDSL.orDefault(false), 230 "subscope", (Optional!string subscope) => subscope.orDefault(""), 231 ); 232 } 233 234 class SpecializeDef 235 { 236 mixin genCanonicalizeBody!(so.SpecializeDef); 237 } 238 239 @documentRoot class SaladEnumSchema 240 { 241 mixin genCanonicalizeBody!( 242 so.SaladEnumSchema, 243 "inVocab", (Optional!bool inVocab) => inVocab.orDefault(true), 244 "doc", (Optional!(string, string[]) doc) => doc.concat, 245 "docChild", (Optional!(string, string[]) docChild) => docChild.concat, 246 "jsonldPredicate", (Optional!(string, so.JsonldPredicate) jp) => jp.canonicalize, 247 "documentRoot", (Optional!bool documentRoot) => documentRoot.orDefault(false), 248 "extends", (Optional!(string, string[]) extends) => extends.match!((string s) => [s], 249 (string[] ss) => ss, 250 none => (string[]).init), 251 ); 252 } 253 254 @documentRoot class Documentation 255 { 256 mixin genCanonicalizeBody!( 257 so.Documentation, 258 "inVocab", (Optional!bool inVocab) => inVocab.orDefault(true), 259 "doc", (Optional!(string, string[]) doc) => doc.concat, 260 "docChild", (Optional!(string, string[]) docChild) => docChild.concat, 261 ); 262 }