1 /++ 2 $(H4 High level YAML deserialization API) 3 4 Macros: 5 IONREF = $(REF_ALTTEXT $(TT $2), $2, mir, ion, $1)$(NBSP) 6 +/ 7 module mir.deser.yaml; 8 9 import std.traits: isMutable; 10 11 /++ 12 Deserialize YAML document to a scpecified type. 13 Params: 14 T = type of the value 15 text = UTF-8 text (without BOM) 16 fileNam = (optional) file name for better error information 17 Returns: 18 value of type `T` 19 +/ 20 T deserializeYaml(T)(scope const(char)[] text, string fileName = "<unknown>") 21 if (isMutable!T) 22 { 23 auto values = deserializeYamlValues!T(text, fileName); 24 25 if (values.length != 1) 26 { 27 import mir.serde: SerdeMirException; 28 throw new SerdeMirException( 29 "Expected single YAML document in file", fileName, 30 ", got ", values.length, " documents"); 31 } 32 33 import core.lifetime: move; 34 return move(values[0]); 35 } 36 37 /// 38 @safe 39 unittest 40 { 41 import mir.test: should; 42 43 static struct S 44 { 45 string foo; 46 uint bar; 47 } 48 49 `{foo: str, bar: 4}`.deserializeYaml!S.should == S("str", 4); 50 } 51 52 /// Tags (annotations) support 53 @safe 54 unittest 55 { 56 import mir.test: should; 57 import mir.algebraic: Variant; 58 import mir.serde: serdeAlgebraicAnnotation, serdeAnnotation, serdeOptional; 59 60 @serdeAlgebraicAnnotation("!S") 61 static struct S 62 { 63 string foo; 64 uint bar; 65 } 66 67 @serdeAlgebraicAnnotation("rgb") 68 static struct RGB 69 { 70 @serdeAnnotation @serdeOptional 71 string name; 72 73 ubyte r, g, b; 74 } 75 76 alias V = Variant!(S, RGB); 77 78 `!S {foo: str, bar: 4}`.deserializeYaml!V.should == V("str", 4); 79 80 // Multiple Ion annotations represented in a single tag using `::`. 81 `!<rgb::dark_blue> {r: 23, g: 25, b: 55}`.deserializeYaml!V.should == V(RGB("dark_blue", 23, 25, 55)); 82 } 83 84 /// YAML-specific deserialization 85 @safe pure 86 unittest 87 { 88 import mir.algebraic_alias.yaml: YamlAlgebraic, YamlMap; 89 import mir.test: should; 90 91 auto yaml = `{foo: str, bar: 4}`; 92 93 auto value = yaml.deserializeYaml!YamlAlgebraic("test.yml"); 94 value.object["bar"].should == 4; 95 value.tag.should == `tag:yaml.org,2002:map`; 96 value.startMark.file.should == "test.yml"; 97 98 99 // `YamlMap`, `YamlAlgebraic[]`, and `Annotated!YamlAlgebraic` 100 // are YAML specific types as well 101 auto object = yaml.deserializeYaml!YamlMap("test.yml"); 102 object["bar"].should == 4; 103 object["bar"].tag.should == `tag:yaml.org,2002:int`; 104 105 assert(value == object); 106 } 107 108 /// YAML-user-specific deserialization 109 @safe pure 110 unittest 111 { 112 import mir.test: should; 113 import mir.algebraic_alias.yaml: YamlAlgebraic; 114 115 static struct MyYamlStruct 116 { 117 YamlAlgebraic node; 118 119 this(YamlAlgebraic node) @safe pure 120 { 121 this.node = node; 122 } 123 } 124 125 auto s = `{foo: str, bar: 4}`.deserializeYaml!MyYamlStruct("test.yml"); 126 s.node.object["bar"].should == 4; 127 s.node.tag.should == `tag:yaml.org,2002:map`; 128 } 129 130 /++ 131 Deserialize YAML documents to an array of scpecified type. 132 Params: 133 T = type of the value 134 text = UTF-8 text (without BOM) 135 fileNam = (optional) file name for better error information 136 Returns: 137 array of type `T` 138 +/ 139 // pure 140 T[] deserializeYamlValues(T)(scope const(char)[] text, string fileName = "<unknown>") 141 if (isMutable!T) 142 { 143 import mir.algebraic_alias.yaml: YamlAlgebraic; 144 import mir.array.allocation: array; 145 import mir.ndslice.topology: map; 146 import std.meta: staticIndexOf; 147 148 static if (is(T == YamlAlgebraic)) 149 { 150 import mir.yaml.internal.loader: Loader; 151 pragma(inline, false); 152 return text.Loader(fileName).loadAll; 153 } 154 else 155 static if (staticIndexOf!(T, YamlAlgebraic.AllowedTypes) >= 0) 156 { 157 return text.deserializeYamlValues!YamlAlgebraic(fileName) 158 .map!((ref value) => value.get!T).array; 159 } 160 else 161 static if (is(typeof(YamlAlgebraic[].init.map!T.array()))) 162 { 163 return text.deserializeYamlValues!YamlAlgebraic(fileName) 164 .map!T.array; 165 } 166 else 167 { 168 import mir.ion.conv: serde; 169 import mir.serde: SerdeTarget; 170 return text.deserializeYamlValues!YamlAlgebraic(fileName) 171 .map!((scope ref const value) => serde!T(value, SerdeTarget.yaml)).array; 172 } 173 }