1 /++ 2 $(H1 Mutable YAML value) 3 4 This module contains a single alias definition and doesn't provide YAML serialization API. 5 6 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 7 Authors: Ilia Ki 8 Macros: 9 +/ 10 module mir.algebraic_alias.yaml; 11 12 import mir.serde: serdeLikeStruct, serdeProxy; 13 14 import mir.algebraic: 15 algVerbose, 16 algMeta, 17 algTransp, 18 Algebraic, 19 This; 20 21 import mir.exception: MirException; 22 23 /// 24 public import mir.annotated: Annotated; 25 /// 26 public import mir.lob: Blob; 27 /// 28 public import mir.timestamp: Timestamp; 29 /// 30 public import mir.parse: ParsePosition; 31 32 private alias AliasSeq(T...) = T; 33 34 ///Scalar styles. 35 enum YamlScalarStyle : ubyte 36 { 37 /// Invalid (uninitialized) style 38 none, 39 /// `|` (Literal block style) 40 literal, 41 /// `>` (Folded block style) 42 folded, 43 /// Plain scalar 44 plain, 45 /// Single quoted scalar 46 singleQuoted, 47 /// Double quoted scalar 48 doubleQuoted 49 } 50 51 ///Collection styles. 52 enum YamlCollectionStyle : ubyte 53 { 54 /// Invalid (uninitialized) style 55 none, 56 /// Block style. 57 block, 58 /// Flow style. 59 flow 60 } 61 62 /++ 63 Definition union for $(LREF YamlAlgebraic). 64 +/ 65 union Yaml_ 66 { 67 /// 68 typeof(null) null_; 69 /// 70 bool boolean; 71 /// 72 long integer; 73 /// 74 double float_; 75 /// 76 immutable(char)[] string; 77 /// 78 Blob blob; 79 /// 80 Timestamp timestamp; 81 /// Self alias in array. 82 This[] array; 83 /// Self alias in $(MREF mir,string_map). 84 YamlMap object; 85 /// Self alias in $(MREF mir,annotated). 86 Annotated!This annotated; 87 88 @algMeta: 89 /// 90 immutable(char)[] tag; 91 @algTransp: 92 /// 93 YamlCollectionStyle collectionStyle; 94 /// 95 YamlScalarStyle scalarStyle; 96 /// 97 @algVerbose ParsePosition startMark; 98 } 99 100 /++ 101 YAML tagged algebraic alias. 102 103 The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic). 104 See also $(MREF mir,string_map) - ordered string-value associative array. 105 +/ 106 alias YamlAlgebraic = Algebraic!Yaml_; 107 108 /++ 109 YAML map representation. 110 111 The implementation preserves order and allows duplicated keys. 112 +/ 113 @serdeLikeStruct 114 @serdeProxy!YamlAlgebraic 115 struct YamlMap 116 { 117 /++ 118 +/ 119 YamlPair[] pairs; 120 121 /// 122 this(YamlPair[] pairs) @safe pure nothrow @nogc 123 { 124 this.pairs = pairs; 125 } 126 127 size_t length() scope const @property @safe pure nothrow @nogc 128 { 129 return pairs.length; 130 } 131 132 static foreach (V; AliasSeq!(YamlAlgebraic.AllowedTypes[1 .. $], YamlAlgebraic, int)) 133 { 134 static foreach (K; AliasSeq!(YamlAlgebraic.AllowedTypes[1 .. $], YamlAlgebraic, int)) 135 /// 136 this(K[] keys, V[] values) @safe pure nothrow 137 in(keys.length == values.length) 138 { 139 import mir.ndslice.topology: zip, map; 140 import mir.array.allocation: array; 141 this.pairs = keys.zip(values).map!YamlPair.array; 142 } 143 144 ref YamlAlgebraic opIndexAssign(V value, string key) @safe pure return scope nothrow 145 { 146 if (auto valuePtr = key in this) 147 return *valuePtr = value; 148 pairs ~= YamlPair(key, value); 149 return pairs[$ - 1].value; 150 } 151 152 ref YamlAlgebraic opIndexAssign(V value, scope const(char)[] key) @safe pure return scope nothrow 153 { 154 if (auto valuePtr = key in this) 155 return *valuePtr = value; 156 pairs ~= YamlPair(key.idup, value); 157 return pairs[$ - 1].value; 158 } 159 160 ref YamlAlgebraic opIndexAssign(V value, YamlAlgebraic key) @safe pure return scope nothrow 161 { 162 if (auto valuePtr = key in this) 163 return *valuePtr = value; 164 pairs ~= YamlPair(key, value); 165 return pairs[$ - 1].value; 166 } 167 } 168 169 /++ 170 +/ 171 this(K, V)(K[V] associativeArray) 172 { 173 import mir.ndslice.topology: map; 174 import mir.array.allocation: array; 175 this.pairs = associativeArray 176 .byKeyValue 177 .map!(kv => YamlPair(kv.key, kv.value)) 178 .array; 179 } 180 181 /++ 182 Returns: the first value associated with the provided key. 183 +/ 184 inout(YamlAlgebraic)* _opIn(scope const char[] key) @safe pure nothrow @nogc inout return scope 185 { 186 foreach (ref pair; pairs) 187 if (pair.key == key) 188 return &pair.value; 189 return null; 190 } 191 192 /// ditto 193 inout(YamlAlgebraic)* _opIn(scope const YamlAlgebraic key) @safe pure nothrow @nogc inout return scope 194 { 195 foreach (ref pair; pairs) 196 if (pair.key == key) 197 return &pair.value; 198 return null; 199 } 200 201 alias opBinaryRight(string op : "in") = _opIn; 202 203 /++ 204 Returns: the first value associated with the provided key. 205 +/ 206 ref inout(YamlAlgebraic) opIndex(scope const char[] key) @safe pure inout return scope 207 { 208 if (auto valuePtr = key in this) 209 return *valuePtr; 210 throw new MirException("YamlMap: can't find key '", key, "'"); 211 } 212 213 /// ditto 214 ref inout(YamlAlgebraic) opIndex(scope const YamlAlgebraic key) @safe pure inout return scope 215 { 216 if (key._is!string) 217 return this[key.get!string]; 218 if (auto valuePtr = key in this) 219 return *valuePtr; 220 import mir.format: stringBuf, getData; 221 auto buf = stringBuf; 222 key.toString(buf); 223 throw new MirException("YamlMap: can't find key ", buf << getData); 224 } 225 226 bool opEquals(scope const typeof(this) rhs) scope const @safe pure nothrow @nogc 227 { 228 return pairs == rhs.pairs; 229 } 230 231 int opCmp(scope const typeof(this) rhs) scope const @safe pure nothrow @nogc 232 { 233 return __cmp(pairs, rhs.pairs); 234 } 235 236 /// 237 auto byKeyValue() @trusted return scope pure nothrow @nogc 238 { 239 import mir.ndslice.slice: sliced; 240 return pairs.sliced; 241 } 242 243 /// ditto 244 auto byKeyValue() const @trusted return scope pure nothrow @nogc 245 { 246 import mir.ndslice.slice: sliced; 247 return pairs.sliced; 248 } 249 250 static import mir.functional; 251 252 /// 253 auto opIndex() @trusted return scope pure nothrow @nogc 254 { 255 import mir.ndslice.slice: sliced; 256 return sliced(cast(mir.functional.Tuple!(YamlAlgebraic, YamlAlgebraic)[]) pairs); 257 } 258 259 /// ditto 260 auto opIndex() const @trusted return scope pure nothrow @nogc 261 { 262 import mir.ndslice.slice: sliced; 263 return sliced(cast(const mir.functional.Tuple!(YamlAlgebraic, YamlAlgebraic)[]) pairs); 264 } 265 } 266 267 /// 268 version(mir_ion_test) 269 unittest 270 { 271 YamlMap map = ["a" : 1]; 272 assert(map["a"] == 1); 273 map[1.YamlAlgebraic] = "a"; 274 map["a"] = 3; 275 map["a"].get!long++; 276 map["a"].get!"integer" += 3; 277 map["a"].integer += 3; 278 assert(map["a"] == 10); 279 assert(map[1.YamlAlgebraic] == "a"); 280 281 // foreach iteration 282 long sum; 283 foreach (ref key, ref value; map) 284 if (key == "a") 285 sum += value.get!long; 286 assert(sum == 10); 287 } 288 289 /++ 290 +/ 291 struct YamlPair 292 { 293 /// 294 YamlAlgebraic key; 295 /// 296 YamlAlgebraic value; 297 298 static foreach (K; AliasSeq!(YamlAlgebraic.AllowedTypes, YamlAlgebraic, int)) 299 static foreach (V; AliasSeq!(YamlAlgebraic.AllowedTypes, YamlAlgebraic, int)) 300 /// 301 this(K key, V value) @safe pure nothrow @nogc 302 { 303 static if (is(K == YamlAlgebraic)) 304 this.key = key; 305 else 306 this.key.__ctor(key); 307 static if (is(V == YamlAlgebraic)) 308 this.value = value; 309 else 310 this.value.__ctor(value); 311 } 312 313 /// 314 int opCmp(ref scope const typeof(this) rhs) scope const @safe pure nothrow @nogc 315 { 316 if (auto d = key.opCmp(rhs.key)) 317 return d; 318 return value.opCmp(rhs.value); 319 } 320 321 /// 322 int opCmp(scope const typeof(this) rhs) scope const @safe pure nothrow @nogc 323 { 324 return this.opCmp(rhs); 325 } 326 } 327 328 /// 329 version(mir_ion_test) 330 unittest 331 { 332 import mir.ndslice.topology: map; 333 import mir.array.allocation: array; 334 335 YamlAlgebraic value; 336 337 // Default 338 assert(value.isNull); 339 assert(value.kind == YamlAlgebraic.Kind.null_); 340 341 // Boolean 342 value = true; 343 344 assert(!value.isNull); 345 assert(value == true); 346 assert(value.kind == YamlAlgebraic.Kind.boolean); 347 assert(value.boolean == true); 348 assert(value.get!bool == true); 349 assert(value.get!(YamlAlgebraic.Kind.boolean) == true); 350 351 // Null 352 value = null; 353 assert(value.isNull); 354 assert(value == null); 355 assert(value.kind == YamlAlgebraic.Kind.null_); 356 assert(value.null_ == null); 357 assert(value.get!(typeof(null)) == null); 358 assert(value.get!(YamlAlgebraic.Kind.null_) == null); 359 360 // String 361 value = "s"; 362 assert(value.kind == YamlAlgebraic.Kind..string); 363 assert(value == "s"); 364 assert(value..string == "s"); 365 assert(value.get!string == "s"); 366 assert(value.get!(YamlAlgebraic.Kind..string) == "s"); 367 368 // Integer 369 value = 4; 370 assert(value.kind == YamlAlgebraic.Kind.integer); 371 assert(value == 4); 372 assert(value != 4.0); 373 assert(value.integer == 4); 374 375 // Float 376 value = 3.0; 377 assert(value.kind == YamlAlgebraic.Kind.float_); 378 assert(value != 3); 379 assert(value == 3.0); 380 assert(value.float_ == 3.0); 381 assert(value.get!double == 3.0); 382 assert(value.get!(YamlAlgebraic.Kind.float_) == 3.0); 383 384 // Array 385 YamlAlgebraic[] arr = [0, 1, 2, 3, 4].map!YamlAlgebraic.array; 386 387 value = arr; 388 assert(value.kind == YamlAlgebraic.Kind.array); 389 assert(value == arr); 390 assert(value.array[3] == 3); 391 392 // Object 393 value = [1 : "a"].YamlAlgebraic; 394 assert(value.kind == YamlAlgebraic.Kind.object); 395 assert(value.object.pairs == [YamlPair(1, "a")]); 396 assert(value.object[1.YamlAlgebraic] == "a"); 397 398 assert(value == value); 399 assert(value <= value); 400 assert(value >= value); 401 }